Skip to content

Commit c774b31

Browse files
committed
Simulator #10
1 parent e593412 commit c774b31

File tree

6 files changed

+135
-13
lines changed

6 files changed

+135
-13
lines changed

lib/src/codelessly.dart

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import '../codelessly_sdk.dart';
1212
import 'logging/reporter.dart';
1313
import 'utils/codelessly_http_client.dart';
1414

15-
typedef NavigationListener = void Function(BuildContext context);
15+
typedef NavigationListener = void Function(
16+
BuildContext context,
17+
String? layoutId,
18+
);
1619
typedef BreakpointsListener = void Function(
1720
BuildContext context, Breakpoint breakpoint);
1821

@@ -148,10 +151,22 @@ class Codelessly {
148151
/// If it is null, it means it has not yet been initialized.
149152
CloudDatabase? get cloudDatabase => dataManager.cloudDatabase;
150153

154+
String? _currentNavigatedLayoutId;
155+
156+
String? get currentNavigatedLayoutId => _currentNavigatedLayoutId;
157+
151158
final Map<String, NavigationListener> _navigationListeners = {};
152159

153160
final Map<String, BreakpointsListener> _breakpointsListeners = {};
154161

162+
final StreamController<BrightnessModel> _systemUIBrightnessStreamController =
163+
StreamController.broadcast()..add(BrightnessModel.system);
164+
165+
/// A stream that produces events whenever a canvas node issues a system UI
166+
/// brightness change. Used for simulation purposes.
167+
Stream<BrightnessModel> get systemUIBrightnessStream =>
168+
_systemUIBrightnessStreamController.stream;
169+
155170
/// Creates a new instance of [Codelessly].
156171
Codelessly({
157172
CodelesslyConfig? config,
@@ -238,6 +253,8 @@ class Codelessly {
238253
_navigationListeners.clear();
239254
_breakpointsListeners.clear();
240255
_client.close();
256+
257+
_systemUIBrightnessStreamController.close();
241258
}
242259

243260
/// Resets the state of the SDK. This is useful for resetting the data without
@@ -746,9 +763,11 @@ class Codelessly {
746763
/// Calls navigation listeners when navigation happens.
747764
/// Provided [context] must be of the destination widget. This context
748765
/// can be used to retrieve new [CodelesslyContext].
749-
void notifyNavigationListeners(BuildContext context) {
766+
void notifyNavigationListeners(BuildContext context, String? layoutId) {
767+
_currentNavigatedLayoutId = layoutId;
768+
750769
for (final listener in [..._navigationListeners.values]) {
751-
listener(context);
770+
listener(context, layoutId);
752771
}
753772
}
754773

@@ -780,4 +799,10 @@ class Codelessly {
780799
void removeBreakpointsListener(String label) {
781800
_breakpointsListeners.remove(label);
782801
}
802+
803+
/// Sets the system UI brightness to the provided [brightness]. Used for
804+
/// simulation purposes.
805+
void setSystemUIBrightness(BrightnessModel brightness) {
806+
_systemUIBrightnessStreamController.add(brightness);
807+
}
783808
}

lib/src/functions/functions_repository.dart

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,10 @@ class FunctionsRepository {
243243
_log('Performing navigation action with params: $parsedParams');
244244

245245
final Codelessly codelessly = context.read<Codelessly>();
246+
final String myLayoutId = codelessly.currentNavigatedLayoutId!;
247+
246248
if (action.navigationType == NavigationType.pop) {
247249
await Navigator.maybePop(context, parsedParams);
248-
if (context.mounted) {
249-
codelessly.notifyNavigationListeners(context);
250-
}
251250
} else {
252251
// Check if a layout exists for the action's [destinationId].
253252
final String? layoutId = codelessly
@@ -292,8 +291,11 @@ class FunctionsRepository {
292291
),
293292
),
294293
);
294+
// Notify navigation listeners that this screen has been navigated back
295+
// to, because the Navigator.push future has completed and came
296+
// back to this screen.
295297
if (context.mounted) {
296-
codelessly.notifyNavigationListeners(context);
298+
codelessly.notifyNavigationListeners(context, myLayoutId);
297299
}
298300
} else if (action.navigationType == NavigationType.replace) {
299301
await Navigator.pushReplacement(
@@ -305,8 +307,12 @@ class FunctionsRepository {
305307
),
306308
),
307309
);
310+
311+
// Notify navigation listeners that this screen has been navigated back
312+
// to, because the Navigator.push future has completed and came
313+
// back to this screen.
308314
if (context.mounted) {
309-
codelessly.notifyNavigationListeners(context);
315+
codelessly.notifyNavigationListeners(context, myLayoutId);
310316
}
311317
}
312318
}
@@ -322,6 +328,8 @@ class FunctionsRepository {
322328
_log('Performing show dialog action with params: $parsedParams');
323329

324330
final Codelessly codelessly = context.read<Codelessly>();
331+
final myLayoutId = codelessly.currentNavigatedLayoutId!;
332+
325333
// Check if a layout exists for the action's [destinationId].
326334
final String? layoutId = codelessly.dataManager.publishModel?.layouts.values
327335
.firstWhereOrNull(
@@ -361,7 +369,7 @@ class FunctionsRepository {
361369
),
362370
);
363371
if (context.mounted) {
364-
codelessly.notifyNavigationListeners(context);
372+
codelessly.notifyNavigationListeners(context, myLayoutId);
365373
}
366374
}
367375

lib/src/transformers/node_transformer.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ class WidgetBuildSettings extends BuildSettings {
8383
/// constraints, etc.
8484
final bool buildRawWidget;
8585

86+
/// Determines the brightness of the current screen for the system UI.
87+
final BrightnessModel brightness;
88+
8689
/// Creates a [WidgetBuildSettings] instance.
8790
const WidgetBuildSettings({
8891
super.withOpacity,
@@ -99,6 +102,7 @@ class WidgetBuildSettings extends BuildSettings {
99102
this.nullSubstitutionMode = NullSubstitutionMode.noChange,
100103
this.replaceVariablesWithSymbols = false,
101104
this.buildRawWidget = false,
105+
this.brightness = BrightnessModel.system,
102106
});
103107

104108
/// Creates a copy of this [WidgetBuildSettings] instance.
@@ -117,6 +121,7 @@ class WidgetBuildSettings extends BuildSettings {
117121
NullSubstitutionMode? nullSubstitutionMode,
118122
bool? replaceVariablesWithSymbols,
119123
bool? buildRawWidget,
124+
BrightnessModel? brightness,
120125
}) {
121126
return WidgetBuildSettings(
122127
withOpacity: withOpacity ?? this.withOpacity,
@@ -134,6 +139,7 @@ class WidgetBuildSettings extends BuildSettings {
134139
replaceVariablesWithSymbols:
135140
replaceVariablesWithSymbols ?? this.replaceVariablesWithSymbols,
136141
buildRawWidget: buildRawWidget ?? this.buildRawWidget,
142+
brightness: brightness ?? this.brightness,
137143
);
138144
}
139145

@@ -144,6 +150,7 @@ class WidgetBuildSettings extends BuildSettings {
144150
nullSubstitutionMode,
145151
replaceVariablesWithSymbols,
146152
buildRawWidget,
153+
brightness,
147154
];
148155
}
149156

lib/src/transformers/node_transformers/passive_canvas_transformer.dart

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import 'dart:async';
22

33
import 'package:codelessly_api/codelessly_api.dart';
44
import 'package:flutter/material.dart';
5+
import 'package:flutter/services.dart';
6+
import 'package:provider/provider.dart';
57

68
import '../../../codelessly_sdk.dart';
79
import '../../functions/functions_repository.dart';
@@ -38,6 +40,7 @@ class PassiveCanvasTransformer extends NodeWidgetTransformer<CanvasNode> {
3840
settings: settings.copyWith(
3941
// Passes through to build a PreferredSizeWidget directly.
4042
buildRawWidget: true,
43+
brightness: node.properties.brightness,
4144
),
4245
);
4346

@@ -51,7 +54,9 @@ class PassiveCanvasTransformer extends NodeWidgetTransformer<CanvasNode> {
5154
final Widget appBarChild = manager.buildWidgetFromNode(
5255
appBarNode,
5356
context,
54-
settings: settings,
57+
settings: settings.copyWith(
58+
brightness: node.properties.brightness,
59+
),
5560
);
5661

5762
return PreferredSize(
@@ -386,6 +391,39 @@ class _PassiveCanvasWidgetState extends State<PassiveCanvasWidget> {
386391

387392
bool didPerformOnLoadActions = false;
388393

394+
Codelessly? codelessly;
395+
396+
@override
397+
void initState() {
398+
super.initState();
399+
400+
codelessly = context.read<Codelessly>();
401+
final String? myLayoutId = codelessly!
402+
.dataManager.publishModel?.layouts.dataMap.entries
403+
.firstWhere((entry) {
404+
return entry.value.canvasIds.contains(widget.node.id);
405+
}).key;
406+
407+
// Set the system UI brightness to the canvas brightness.
408+
codelessly!.setSystemUIBrightness(widget.node.properties.brightness);
409+
410+
// Listen for navigation events to update the system UI brightness back
411+
// again, such as if this canvas was navigated away from, but then the view
412+
// pops and goes back to this canvas.
413+
codelessly!.addNavigationListener('canvas-${widget.node.id}',
414+
(event, layoutId) {
415+
if (myLayoutId == layoutId) {
416+
codelessly!.setSystemUIBrightness(widget.node.properties.brightness);
417+
}
418+
});
419+
}
420+
421+
@override
422+
void dispose() {
423+
codelessly?.removeNavigationListener('canvas-${widget.node.id}');
424+
super.dispose();
425+
}
426+
389427
@override
390428
void didUpdateWidget(covariant PassiveCanvasWidget oldWidget) {
391429
super.didUpdateWidget(oldWidget);
@@ -456,5 +494,26 @@ class _PassiveCanvasWidgetState extends State<PassiveCanvasWidget> {
456494
}
457495

458496
@override
459-
Widget build(BuildContext context) => widget.builder(context);
497+
Widget build(BuildContext context) {
498+
final Brightness brightness =
499+
widget.node.properties.brightness.toFlutterBrightness(context);
500+
final SystemUiOverlayStyle? systemOverlayStyle =
501+
Theme.of(context).appBarTheme.systemOverlayStyle;
502+
return Theme(
503+
data: Theme.of(context).copyWith(
504+
appBarTheme: systemOverlayStyle != null
505+
? null
506+
: Theme.of(context).appBarTheme.copyWith(
507+
systemOverlayStyle: SystemUiOverlayStyle(
508+
statusBarColor: brightness == Brightness.light
509+
? Colors.black
510+
: Colors.white,
511+
statusBarIconBrightness: brightness,
512+
statusBarBrightness: brightness,
513+
),
514+
),
515+
),
516+
child: widget.builder(context),
517+
);
518+
}
460519
}

lib/src/ui/codelessly_widget.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,8 @@ class _CodelesslyWidgetState extends State<CodelesslyWidget> {
623623
],
624624
child: _NavigationBuilder(
625625
key: ValueKey(_effectiveController.layoutID),
626+
// TODO(Saad): Ensure this is not null on first load in non-simulated cases.
627+
layoutId: _effectiveController.layoutID,
626628
builder: (context) {
627629
return StreamBuilder<CStatus>(
628630
stream: codelessly.statusStream,
@@ -662,8 +664,13 @@ class _CodelesslyWidgetState extends State<CodelesslyWidget> {
662664

663665
class _NavigationBuilder extends StatefulWidget {
664666
final WidgetBuilder builder;
667+
final String? layoutId;
665668

666-
const _NavigationBuilder({super.key, required this.builder});
669+
const _NavigationBuilder({
670+
super.key,
671+
required this.builder,
672+
required this.layoutId,
673+
});
667674

668675
@override
669676
State<_NavigationBuilder> createState() => _NavigationBuilderState();
@@ -675,7 +682,10 @@ class _NavigationBuilderState extends State<_NavigationBuilder> {
675682
super.initState();
676683
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
677684
if (!context.mounted) return;
678-
context.read<Codelessly>().notifyNavigationListeners(context);
685+
context.read<Codelessly>().notifyNavigationListeners(
686+
context,
687+
widget.layoutId,
688+
);
679689
});
680690
}
681691

lib/src/utils/extensions.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,19 @@ extension ColorHelper on flutter.Color {
330330
);
331331
}
332332

333+
extension BrightnessModelHelper on BrightnessModel {
334+
Brightness toFlutterBrightness(BuildContext context) {
335+
switch (this) {
336+
case BrightnessModel.dark:
337+
return Brightness.dark;
338+
case BrightnessModel.light:
339+
return Brightness.light;
340+
case BrightnessModel.system:
341+
return MediaQuery.of(context).platformBrightness;
342+
}
343+
}
344+
}
345+
333346
extension ColorRGBAHelper on ColorRGBA {
334347
Color toFlutterColor() {
335348
var ir = (r * 255).toInt();

0 commit comments

Comments
 (0)