1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/material.dart';
6///
7/// @docImport 'app.dart';
8/// @docImport 'button.dart';
9/// @docImport 'switch.dart';
10library;
11
12import 'package:flutter/foundation.dart';
13import 'package:flutter/widgets.dart';
14
15import 'colors.dart';
16import 'icon_theme_data.dart';
17import 'text_theme.dart';
18
19export 'package:flutter/foundation.dart' show Brightness;
20
21// Values derived from https://developer.apple.com/design/resources/.
22const _CupertinoThemeDefaults _kDefaultTheme = _CupertinoThemeDefaults(
23 null,
24 CupertinoColors.systemBlue,
25 CupertinoColors.white,
26 CupertinoDynamicColor.withBrightness(
27 color: Color(0xF0F9F9F9),
28 darkColor: Color(0xF01D1D1D),
29 // Values extracted from navigation bar. For toolbar or tabbar the dark color is 0xF0161616.
30 ),
31 CupertinoColors.systemBackground,
32 false,
33 _CupertinoTextThemeDefaults(CupertinoColors.label, CupertinoColors.inactiveGray),
34);
35
36/// Applies a visual styling theme to descendant Cupertino widgets.
37///
38/// Affects the color and text styles of Cupertino widgets whose styling
39/// are not overridden when constructing the respective widgets instances.
40///
41/// Descendant widgets can retrieve the current [CupertinoThemeData] by calling
42/// [CupertinoTheme.of]. An [InheritedWidget] dependency is created when
43/// an ancestor [CupertinoThemeData] is retrieved via [CupertinoTheme.of].
44///
45/// The [CupertinoTheme] widget implies an [IconTheme] widget, whose
46/// [IconTheme.data] has the same color as [CupertinoThemeData.primaryColor]
47///
48/// See also:
49///
50/// * [CupertinoThemeData], specifies the theme's visual styling.
51/// * [CupertinoApp], which will automatically add a [CupertinoTheme] based on the
52/// value of [CupertinoApp.theme].
53/// * [Theme], a Material theme which will automatically add a [CupertinoTheme]
54/// with a [CupertinoThemeData] derived from the Material [ThemeData].
55class CupertinoTheme extends StatelessWidget {
56 /// Creates a [CupertinoTheme] to change descendant Cupertino widgets' styling.
57 const CupertinoTheme({super.key, required this.data, required this.child});
58
59 /// The [CupertinoThemeData] styling for this theme.
60 final CupertinoThemeData data;
61
62 /// Retrieves the [CupertinoThemeData] from the closest ancestor [CupertinoTheme]
63 /// widget, or a default [CupertinoThemeData] if no [CupertinoTheme] ancestor
64 /// exists.
65 ///
66 /// Resolves all the colors defined in that [CupertinoThemeData] against the
67 /// given [BuildContext] on a best-effort basis.
68 static CupertinoThemeData of(BuildContext context) {
69 final InheritedCupertinoTheme? inheritedTheme =
70 context.dependOnInheritedWidgetOfExactType<InheritedCupertinoTheme>();
71 return (inheritedTheme?.theme.data ?? const CupertinoThemeData()).resolveFrom(context);
72 }
73
74 /// Retrieves the [Brightness] to use for descendant Cupertino widgets, based
75 /// on the value of [CupertinoThemeData.brightness] in the given [context].
76 ///
77 /// If no [CupertinoTheme] can be found in the given [context], or its `brightness`
78 /// is null, it will fall back to [MediaQueryData.platformBrightness].
79 ///
80 /// Throws an exception if no valid [CupertinoTheme] or [MediaQuery] widgets
81 /// exist in the ancestry tree.
82 ///
83 /// See also:
84 ///
85 /// * [maybeBrightnessOf], which returns null if no valid [CupertinoTheme] or
86 /// [MediaQuery] exists, instead of throwing.
87 /// * [CupertinoThemeData.brightness], the property takes precedence over
88 /// [MediaQueryData.platformBrightness] for descendant Cupertino widgets.
89 static Brightness brightnessOf(BuildContext context) {
90 final InheritedCupertinoTheme? inheritedTheme =
91 context.dependOnInheritedWidgetOfExactType<InheritedCupertinoTheme>();
92 return inheritedTheme?.theme.data.brightness ?? MediaQuery.platformBrightnessOf(context);
93 }
94
95 /// Retrieves the [Brightness] to use for descendant Cupertino widgets, based
96 /// on the value of [CupertinoThemeData.brightness] in the given [context].
97 ///
98 /// If no [CupertinoTheme] can be found in the given [context], it will fall
99 /// back to [MediaQueryData.platformBrightness].
100 ///
101 /// Returns null if no valid [CupertinoTheme] or [MediaQuery] widgets exist in
102 /// the ancestry tree.
103 ///
104 /// See also:
105 ///
106 /// * [CupertinoThemeData.brightness], the property takes precedence over
107 /// [MediaQueryData.platformBrightness] for descendant Cupertino widgets.
108 /// * [brightnessOf], which throws if no valid [CupertinoTheme] or
109 /// [MediaQuery] exists, instead of returning null.
110 static Brightness? maybeBrightnessOf(BuildContext context) {
111 final InheritedCupertinoTheme? inheritedTheme =
112 context.dependOnInheritedWidgetOfExactType<InheritedCupertinoTheme>();
113 return inheritedTheme?.theme.data.brightness ?? MediaQuery.maybePlatformBrightnessOf(context);
114 }
115
116 /// The widget below this widget in the tree.
117 ///
118 /// {@macro flutter.widgets.ProxyWidget.child}
119 final Widget child;
120
121 @override
122 Widget build(BuildContext context) {
123 return InheritedCupertinoTheme(
124 theme: this,
125 child: IconTheme(data: CupertinoIconThemeData(color: data.primaryColor), child: child),
126 );
127 }
128
129 @override
130 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
131 super.debugFillProperties(properties);
132 data.debugFillProperties(properties);
133 }
134}
135
136/// Provides a [CupertinoTheme] to all descendents.
137class InheritedCupertinoTheme extends InheritedTheme {
138 /// Creates an [InheritedTheme] that provides a [CupertinoTheme] to all
139 /// descendents.
140 const InheritedCupertinoTheme({super.key, required this.theme, required super.child});
141
142 /// The [CupertinoTheme] that is provided to widgets lower in the tree.
143 final CupertinoTheme theme;
144
145 @override
146 Widget wrap(BuildContext context, Widget child) {
147 return CupertinoTheme(data: theme.data, child: child);
148 }
149
150 @override
151 bool updateShouldNotify(InheritedCupertinoTheme oldWidget) => theme.data != oldWidget.theme.data;
152}
153
154/// Styling specifications for a [CupertinoTheme].
155///
156/// All constructor parameters can be null, in which case a
157/// [CupertinoColors.activeBlue] based default iOS theme styling is used.
158///
159/// Parameters can also be partially specified, in which case some parameters
160/// will cascade down to other dependent parameters to create a cohesive
161/// visual effect. For instance, if a [primaryColor] is specified, it would
162/// cascade down to affect some fonts in [textTheme] if [textTheme] is not
163/// specified.
164///
165/// See also:
166///
167/// * [CupertinoTheme], in which this [CupertinoThemeData] is inserted.
168/// * [ThemeData], a Material equivalent that also configures Cupertino
169/// styling via a [CupertinoThemeData] subclass [MaterialBasedCupertinoThemeData].
170@immutable
171class CupertinoThemeData extends NoDefaultCupertinoThemeData with Diagnosticable {
172 /// Creates a [CupertinoTheme] styling specification.
173 ///
174 /// Unspecified parameters default to a reasonable iOS default style.
175 const CupertinoThemeData({
176 Brightness? brightness,
177 Color? primaryColor,
178 Color? primaryContrastingColor,
179 CupertinoTextThemeData? textTheme,
180 Color? barBackgroundColor,
181 Color? scaffoldBackgroundColor,
182 bool? applyThemeToAll,
183 }) : this.raw(
184 brightness,
185 primaryColor,
186 primaryContrastingColor,
187 textTheme,
188 barBackgroundColor,
189 scaffoldBackgroundColor,
190 applyThemeToAll,
191 );
192
193 /// Same as the default constructor but with positional arguments to avoid
194 /// forgetting any and to specify all arguments.
195 ///
196 /// Used by subclasses to get the superclass's defaulting behaviors.
197 @protected
198 const CupertinoThemeData.raw(
199 Brightness? brightness,
200 Color? primaryColor,
201 Color? primaryContrastingColor,
202 CupertinoTextThemeData? textTheme,
203 Color? barBackgroundColor,
204 Color? scaffoldBackgroundColor,
205 bool? applyThemeToAll,
206 ) : this._rawWithDefaults(
207 brightness,
208 primaryColor,
209 primaryContrastingColor,
210 textTheme,
211 barBackgroundColor,
212 scaffoldBackgroundColor,
213 applyThemeToAll,
214 _kDefaultTheme,
215 );
216
217 const CupertinoThemeData._rawWithDefaults(
218 Brightness? brightness,
219 Color? primaryColor,
220 Color? primaryContrastingColor,
221 CupertinoTextThemeData? textTheme,
222 Color? barBackgroundColor,
223 Color? scaffoldBackgroundColor,
224 bool? applyThemeToAll,
225 this._defaults,
226 ) : super(
227 brightness: brightness,
228 primaryColor: primaryColor,
229 primaryContrastingColor: primaryContrastingColor,
230 textTheme: textTheme,
231 barBackgroundColor: barBackgroundColor,
232 scaffoldBackgroundColor: scaffoldBackgroundColor,
233 applyThemeToAll: applyThemeToAll,
234 );
235
236 final _CupertinoThemeDefaults _defaults;
237
238 @override
239 Color get primaryColor => super.primaryColor ?? _defaults.primaryColor;
240
241 @override
242 Color get primaryContrastingColor =>
243 super.primaryContrastingColor ?? _defaults.primaryContrastingColor;
244
245 @override
246 CupertinoTextThemeData get textTheme {
247 return super.textTheme ??
248 _defaults.textThemeDefaults.createDefaults(primaryColor: primaryColor);
249 }
250
251 @override
252 Color get barBackgroundColor => super.barBackgroundColor ?? _defaults.barBackgroundColor;
253
254 @override
255 Color get scaffoldBackgroundColor =>
256 super.scaffoldBackgroundColor ?? _defaults.scaffoldBackgroundColor;
257
258 @override
259 bool get applyThemeToAll => super.applyThemeToAll ?? _defaults.applyThemeToAll;
260
261 @override
262 NoDefaultCupertinoThemeData noDefault() {
263 return NoDefaultCupertinoThemeData(
264 brightness: super.brightness,
265 primaryColor: super.primaryColor,
266 primaryContrastingColor: super.primaryContrastingColor,
267 textTheme: super.textTheme,
268 barBackgroundColor: super.barBackgroundColor,
269 scaffoldBackgroundColor: super.scaffoldBackgroundColor,
270 applyThemeToAll: super.applyThemeToAll,
271 );
272 }
273
274 @override
275 CupertinoThemeData resolveFrom(BuildContext context) {
276 Color? convertColor(Color? color) => CupertinoDynamicColor.maybeResolve(color, context);
277
278 return CupertinoThemeData._rawWithDefaults(
279 brightness,
280 convertColor(super.primaryColor),
281 convertColor(super.primaryContrastingColor),
282 super.textTheme?.resolveFrom(context),
283 convertColor(super.barBackgroundColor),
284 convertColor(super.scaffoldBackgroundColor),
285 applyThemeToAll,
286 _defaults.resolveFrom(context, super.textTheme == null),
287 );
288 }
289
290 @override
291 CupertinoThemeData copyWith({
292 Brightness? brightness,
293 Color? primaryColor,
294 Color? primaryContrastingColor,
295 CupertinoTextThemeData? textTheme,
296 Color? barBackgroundColor,
297 Color? scaffoldBackgroundColor,
298 bool? applyThemeToAll,
299 }) {
300 return CupertinoThemeData._rawWithDefaults(
301 brightness ?? super.brightness,
302 primaryColor ?? super.primaryColor,
303 primaryContrastingColor ?? super.primaryContrastingColor,
304 textTheme ?? super.textTheme,
305 barBackgroundColor ?? super.barBackgroundColor,
306 scaffoldBackgroundColor ?? super.scaffoldBackgroundColor,
307 applyThemeToAll ?? super.applyThemeToAll,
308 _defaults,
309 );
310 }
311
312 @override
313 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
314 super.debugFillProperties(properties);
315 const CupertinoThemeData defaultData = CupertinoThemeData();
316 properties.add(EnumProperty<Brightness>('brightness', brightness, defaultValue: null));
317 properties.add(
318 createCupertinoColorProperty(
319 'primaryColor',
320 primaryColor,
321 defaultValue: defaultData.primaryColor,
322 ),
323 );
324 properties.add(
325 createCupertinoColorProperty(
326 'primaryContrastingColor',
327 primaryContrastingColor,
328 defaultValue: defaultData.primaryContrastingColor,
329 ),
330 );
331 properties.add(
332 createCupertinoColorProperty(
333 'barBackgroundColor',
334 barBackgroundColor,
335 defaultValue: defaultData.barBackgroundColor,
336 ),
337 );
338 properties.add(
339 createCupertinoColorProperty(
340 'scaffoldBackgroundColor',
341 scaffoldBackgroundColor,
342 defaultValue: defaultData.scaffoldBackgroundColor,
343 ),
344 );
345 properties.add(
346 DiagnosticsProperty<bool>(
347 'applyThemeToAll',
348 applyThemeToAll,
349 defaultValue: defaultData.applyThemeToAll,
350 ),
351 );
352 textTheme.debugFillProperties(properties);
353 }
354
355 @override
356 bool operator ==(Object other) {
357 if (identical(this, other)) {
358 return true;
359 }
360 if (other.runtimeType != runtimeType) {
361 return false;
362 }
363 return other is CupertinoThemeData &&
364 other.brightness == brightness &&
365 other.primaryColor == primaryColor &&
366 other.primaryContrastingColor == primaryContrastingColor &&
367 other.textTheme == textTheme &&
368 other.barBackgroundColor == barBackgroundColor &&
369 other.scaffoldBackgroundColor == scaffoldBackgroundColor &&
370 other.applyThemeToAll == applyThemeToAll;
371 }
372
373 @override
374 int get hashCode => Object.hash(
375 brightness,
376 primaryColor,
377 primaryContrastingColor,
378 textTheme,
379 barBackgroundColor,
380 scaffoldBackgroundColor,
381 applyThemeToAll,
382 );
383}
384
385/// Styling specifications for a cupertino theme without default values for
386/// unspecified properties.
387///
388/// Unlike [CupertinoThemeData] instances of this class do not return default
389/// values for properties that have been left unspecified in the constructor.
390/// Instead, unspecified properties will return null. This is used by
391/// Material's [ThemeData.cupertinoOverrideTheme].
392///
393/// See also:
394///
395/// * [CupertinoThemeData], which uses reasonable default values for
396/// unspecified theme properties.
397class NoDefaultCupertinoThemeData {
398 /// Creates a [NoDefaultCupertinoThemeData] styling specification.
399 ///
400 /// Unspecified properties default to null.
401 const NoDefaultCupertinoThemeData({
402 this.brightness,
403 this.primaryColor,
404 this.primaryContrastingColor,
405 this.textTheme,
406 this.barBackgroundColor,
407 this.scaffoldBackgroundColor,
408 this.applyThemeToAll,
409 });
410
411 /// The brightness override for Cupertino descendants.
412 ///
413 /// Defaults to null. If a non-null [Brightness] is specified, the value will
414 /// take precedence over the ambient [MediaQueryData.platformBrightness], when
415 /// determining the brightness of descendant Cupertino widgets.
416 ///
417 /// If coming from a Material [Theme] and unspecified, [brightness] will be
418 /// derived from the Material [ThemeData]'s [brightness].
419 ///
420 /// See also:
421 ///
422 /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers
423 /// [brightness] to its Material [Theme] parent if it's unspecified.
424 ///
425 /// * [CupertinoTheme.brightnessOf], a method used to retrieve the overall
426 /// [Brightness] from a [BuildContext], for Cupertino widgets.
427 final Brightness? brightness;
428
429 /// A color used on interactive elements of the theme.
430 ///
431 /// This color is generally used on text and icons in buttons and tappable
432 /// elements. Defaults to [CupertinoColors.activeBlue].
433 ///
434 /// If coming from a Material [Theme] and unspecified, [primaryColor] will be
435 /// derived from the Material [ThemeData]'s `colorScheme.primary`. However, in
436 /// iOS styling, the [primaryColor] is more sparsely used than in Material
437 /// Design where the [primaryColor] can appear on non-interactive surfaces like
438 /// the [AppBar] background, [TextField] borders etc.
439 ///
440 /// See also:
441 ///
442 /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers
443 /// [primaryColor] to its Material [Theme] parent if it's unspecified.
444 final Color? primaryColor;
445
446 /// A color that must be easy to see when rendered on a [primaryColor] background.
447 ///
448 /// For example, this color is used for a [CupertinoButton]'s text and icons
449 /// when the button's background is [primaryColor].
450 ///
451 /// If coming from a Material [Theme] and unspecified, [primaryContrastingColor]
452 /// will be derived from the Material [ThemeData]'s `colorScheme.onPrimary`.
453 ///
454 /// See also:
455 ///
456 /// * [MaterialBasedCupertinoThemeData], a [CupertinoThemeData] that defers
457 /// [primaryContrastingColor] to its Material [Theme] parent if it's unspecified.
458 final Color? primaryContrastingColor;
459
460 /// Text styles used by Cupertino widgets.
461 ///
462 /// Derived from [primaryColor] if unspecified.
463 final CupertinoTextThemeData? textTheme;
464
465 /// Background color of the top nav bar and bottom tab bar.
466 ///
467 /// Defaults to a light gray in light mode, or a dark translucent gray color in
468 /// dark mode.
469 final Color? barBackgroundColor;
470
471 /// Background color of the scaffold.
472 ///
473 /// Defaults to [CupertinoColors.systemBackground].
474 final Color? scaffoldBackgroundColor;
475
476 /// Flag to apply this theme to all descendant Cupertino widgets.
477 ///
478 /// Certain Cupertino widgets previously didn't use theming, matching past
479 /// versions of iOS. For example, [CupertinoSwitch]s always used
480 /// [CupertinoColors.systemGreen] when active.
481 ///
482 /// Today, however, these widgets can indeed be themed on iOS. Moreover on
483 /// macOS, the accent color is reflected in these widgets. Turning this flag
484 /// on ensures that descendant Cupertino widgets will be themed accordingly.
485 ///
486 /// This flag currently applies to the following widgets:
487 /// - [CupertinoSwitch] & [Switch.adaptive]
488 ///
489 /// Defaults to false.
490 final bool? applyThemeToAll;
491
492 /// Returns an instance of the theme data whose property getters only return
493 /// the construction time specifications with no derived values.
494 ///
495 /// Used in Material themes to let unspecified properties fallback to Material
496 /// theme properties instead of iOS defaults.
497 NoDefaultCupertinoThemeData noDefault() => this;
498
499 /// Returns a new theme data with all its colors resolved against the
500 /// given [BuildContext].
501 ///
502 /// Called by [CupertinoTheme.of] to resolve colors defined in the retrieved
503 /// [CupertinoThemeData].
504 @protected
505 NoDefaultCupertinoThemeData resolveFrom(BuildContext context) {
506 Color? convertColor(Color? color) => CupertinoDynamicColor.maybeResolve(color, context);
507
508 return NoDefaultCupertinoThemeData(
509 brightness: brightness,
510 primaryColor: convertColor(primaryColor),
511 primaryContrastingColor: convertColor(primaryContrastingColor),
512 textTheme: textTheme?.resolveFrom(context),
513 barBackgroundColor: convertColor(barBackgroundColor),
514 scaffoldBackgroundColor: convertColor(scaffoldBackgroundColor),
515 applyThemeToAll: applyThemeToAll,
516 );
517 }
518
519 /// Creates a copy of the theme data with specified attributes overridden.
520 ///
521 /// Only the current instance's specified attributes are copied instead of
522 /// derived values. For instance, if the current [textTheme] is implied from
523 /// the current [primaryColor] because it was not specified, copying with a
524 /// different [primaryColor] will also change the copy's implied [textTheme].
525 NoDefaultCupertinoThemeData copyWith({
526 Brightness? brightness,
527 Color? primaryColor,
528 Color? primaryContrastingColor,
529 CupertinoTextThemeData? textTheme,
530 Color? barBackgroundColor,
531 Color? scaffoldBackgroundColor,
532 bool? applyThemeToAll,
533 }) {
534 return NoDefaultCupertinoThemeData(
535 brightness: brightness ?? this.brightness,
536 primaryColor: primaryColor ?? this.primaryColor,
537 primaryContrastingColor: primaryContrastingColor ?? this.primaryContrastingColor,
538 textTheme: textTheme ?? this.textTheme,
539 barBackgroundColor: barBackgroundColor ?? this.barBackgroundColor,
540 scaffoldBackgroundColor: scaffoldBackgroundColor ?? this.scaffoldBackgroundColor,
541 applyThemeToAll: applyThemeToAll ?? this.applyThemeToAll,
542 );
543 }
544}
545
546@immutable
547class _CupertinoThemeDefaults {
548 const _CupertinoThemeDefaults(
549 this.brightness,
550 this.primaryColor,
551 this.primaryContrastingColor,
552 this.barBackgroundColor,
553 this.scaffoldBackgroundColor,
554 this.applyThemeToAll,
555 this.textThemeDefaults,
556 );
557
558 final Brightness? brightness;
559 final Color primaryColor;
560 final Color primaryContrastingColor;
561 final Color barBackgroundColor;
562 final Color scaffoldBackgroundColor;
563 final bool applyThemeToAll;
564 final _CupertinoTextThemeDefaults textThemeDefaults;
565
566 _CupertinoThemeDefaults resolveFrom(BuildContext context, bool resolveTextTheme) {
567 Color convertColor(Color color) => CupertinoDynamicColor.resolve(color, context);
568
569 return _CupertinoThemeDefaults(
570 brightness,
571 convertColor(primaryColor),
572 convertColor(primaryContrastingColor),
573 convertColor(barBackgroundColor),
574 convertColor(scaffoldBackgroundColor),
575 applyThemeToAll,
576 resolveTextTheme ? textThemeDefaults.resolveFrom(context) : textThemeDefaults,
577 );
578 }
579}
580
581@immutable
582class _CupertinoTextThemeDefaults {
583 const _CupertinoTextThemeDefaults(this.labelColor, this.inactiveGray);
584
585 final Color labelColor;
586 final Color inactiveGray;
587
588 _CupertinoTextThemeDefaults resolveFrom(BuildContext context) {
589 return _CupertinoTextThemeDefaults(
590 CupertinoDynamicColor.resolve(labelColor, context),
591 CupertinoDynamicColor.resolve(inactiveGray, context),
592 );
593 }
594
595 CupertinoTextThemeData createDefaults({required Color primaryColor}) {
596 return _DefaultCupertinoTextThemeData(
597 primaryColor: primaryColor,
598 labelColor: labelColor,
599 inactiveGray: inactiveGray,
600 );
601 }
602}
603
604// CupertinoTextThemeData with no text styles explicitly specified.
605// The implementation of this class may need to be updated when any of the default
606// text styles changes.
607class _DefaultCupertinoTextThemeData extends CupertinoTextThemeData {
608 const _DefaultCupertinoTextThemeData({
609 required this.labelColor,
610 required this.inactiveGray,
611 required super.primaryColor,
612 });
613
614 final Color labelColor;
615 final Color inactiveGray;
616
617 @override
618 TextStyle get textStyle => super.textStyle.copyWith(color: labelColor);
619
620 @override
621 TextStyle get tabLabelTextStyle => super.tabLabelTextStyle.copyWith(color: inactiveGray);
622
623 @override
624 TextStyle get navTitleTextStyle => super.navTitleTextStyle.copyWith(color: labelColor);
625
626 @override
627 TextStyle get navLargeTitleTextStyle => super.navLargeTitleTextStyle.copyWith(color: labelColor);
628
629 @override
630 TextStyle get pickerTextStyle => super.pickerTextStyle.copyWith(color: labelColor);
631
632 @override
633 TextStyle get dateTimePickerTextStyle =>
634 super.dateTimePickerTextStyle.copyWith(color: labelColor);
635}
636

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com