flutter_flow_model.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. import 'package:collection/collection.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter/scheduler.dart';
  4. import 'package:provider/provider.dart';
  5. import 'flutter_flow_util.dart';
  6. Widget wrapWithModel<T extends FlutterFlowModel>({
  7. required T model,
  8. required Widget child,
  9. required VoidCallback updateCallback,
  10. bool updateOnChange = false,
  11. }) {
  12. // Set the component to optionally update the page on updates.
  13. model.setOnUpdate(
  14. onUpdate: updateCallback,
  15. updateOnChange: updateOnChange,
  16. );
  17. // Models for components within a page will be disposed by the page's model,
  18. // so we don't want the component widget to dispose them until the page is
  19. // itself disposed.
  20. model.disposeOnWidgetDisposal = false;
  21. // Wrap in a Provider so that the model can be accessed by the component.
  22. return Provider<T>.value(
  23. value: model,
  24. child: child,
  25. );
  26. }
  27. T createModel<T extends FlutterFlowModel>(
  28. BuildContext context,
  29. T Function() defaultBuilder,
  30. ) {
  31. final model = context.read<T?>() ?? defaultBuilder();
  32. model._init(context);
  33. return model;
  34. }
  35. abstract class FlutterFlowModel<W extends Widget> {
  36. // Initialization methods
  37. bool _isInitialized = false;
  38. void initState(BuildContext context);
  39. void _init(BuildContext context) {
  40. if (!_isInitialized) {
  41. initState(context);
  42. _isInitialized = true;
  43. }
  44. if (context.widget is W) _widget = context.widget as W;
  45. _context = context;
  46. }
  47. // The widget associated with this model. This is useful for accessing the
  48. // parameters of the widget, for example.
  49. W? _widget;
  50. W? get widget => _widget;
  51. void set widget(W? newWidget) {
  52. _widget = newWidget;
  53. }
  54. // The context associated with this model.
  55. BuildContext? _context;
  56. BuildContext? get context => _context;
  57. // Dispose methods
  58. // Whether to dispose this model when the corresponding widget is
  59. // disposed. By default this is true for pages and false for components,
  60. // as page/component models handle the disposal of their children.
  61. bool disposeOnWidgetDisposal = true;
  62. void dispose();
  63. void maybeDispose() {
  64. if (disposeOnWidgetDisposal) {
  65. dispose();
  66. }
  67. // Remove reference to widget for garbage collection purposes.
  68. _widget = null;
  69. }
  70. // Whether to update the containing page / component on updates.
  71. bool updateOnChange = false;
  72. // Function to call when the model receives an update.
  73. VoidCallback _updateCallback = () {};
  74. void onUpdate() => updateOnChange ? _updateCallback() : () {};
  75. FlutterFlowModel setOnUpdate({
  76. bool updateOnChange = false,
  77. required VoidCallback onUpdate,
  78. }) =>
  79. this
  80. .._updateCallback = onUpdate
  81. ..updateOnChange = updateOnChange;
  82. // Update the containing page when this model received an update.
  83. void updatePage(VoidCallback callback) {
  84. callback();
  85. _updateCallback();
  86. }
  87. FlutterFlowModel get rootModel => context != null
  88. ? DebugFlutterFlowModelContext.maybeOf(context!)?.rootModel ?? this
  89. : this;
  90. WidgetClassDebugData toWidgetClassDebugData() => WidgetClassDebugData();
  91. bool? _isRouteVisible;
  92. bool get isRouteVisible => rootModel._isRouteVisible ?? false;
  93. set isRouteVisible(bool? value) => _isRouteVisible = value;
  94. }
  95. class FlutterFlowDynamicModels<T extends FlutterFlowModel> {
  96. FlutterFlowDynamicModels(this.defaultBuilder);
  97. final T Function() defaultBuilder;
  98. final Map<String, T> _childrenModels = {};
  99. final Map<String, int> _childrenIndexes = {};
  100. Set<String>? _activeKeys;
  101. T getModel(String uniqueKey, int index) {
  102. _updateActiveKeys(uniqueKey);
  103. _childrenIndexes[uniqueKey] = index;
  104. return _childrenModels[uniqueKey] ??= defaultBuilder();
  105. }
  106. List<S> getValues<S>(S? Function(T) getValue) {
  107. return _childrenIndexes.entries
  108. // Sort keys by index.
  109. .sorted((a, b) => a.value.compareTo(b.value))
  110. .where((e) => _childrenModels[e.key] != null)
  111. // Map each model to the desired value and return as list. In order
  112. // to preserve index order, rather than removing null values we provide
  113. // default values (for types with reasonable defaults).
  114. .map((e) => getValue(_childrenModels[e.key]!) ?? _getDefaultValue<S>()!)
  115. .toList();
  116. }
  117. S? getValueAtIndex<S>(int index, S? Function(T) getValue) {
  118. final uniqueKey =
  119. _childrenIndexes.entries.firstWhereOrNull((e) => e.value == index)?.key;
  120. return getValueForKey(uniqueKey, getValue);
  121. }
  122. S? getValueForKey<S>(String? uniqueKey, S? Function(T) getValue) {
  123. final model = _childrenModels[uniqueKey];
  124. return model != null ? getValue(model) : null;
  125. }
  126. void dispose() => _childrenModels.values.forEach((model) => model.dispose());
  127. void _updateActiveKeys(String uniqueKey) {
  128. final shouldResetActiveKeys = _activeKeys == null;
  129. _activeKeys ??= {};
  130. _activeKeys!.add(uniqueKey);
  131. if (shouldResetActiveKeys) {
  132. // Add a post-frame callback to remove and dispose of unused models after
  133. // we're done building, then reset `_activeKeys` to null so we know to do
  134. // this again next build.
  135. SchedulerBinding.instance.addPostFrameCallback((_) {
  136. _childrenIndexes.removeWhere((k, _) => !_activeKeys!.contains(k));
  137. _childrenModels.keys
  138. .toSet()
  139. .difference(_activeKeys!)
  140. // Remove and dispose of unused models since they are not being used
  141. // elsewhere and would not otherwise be disposed.
  142. .forEach((k) => _childrenModels.remove(k)?.maybeDispose());
  143. _activeKeys = null;
  144. });
  145. }
  146. }
  147. DynamicWidgetClassDebugData toDynamicWidgetClassDebugData() =>
  148. DynamicWidgetClassDebugData(
  149. componentStates: _childrenModels
  150. .map((key, value) =>
  151. MapEntry('Key(${key})', value.toWidgetClassDebugData()))
  152. .entries,
  153. );
  154. }
  155. T? _getDefaultValue<T>() {
  156. switch (T) {
  157. case int:
  158. return 0 as T;
  159. case double:
  160. return 0.0 as T;
  161. case String:
  162. return '' as T;
  163. case bool:
  164. return false as T;
  165. default:
  166. return null as T;
  167. }
  168. }
  169. extension TextValidationExtensions on String? Function(BuildContext, String?)? {
  170. String? Function(String?)? asValidator(BuildContext context) =>
  171. this != null ? (val) => this!(context, val) : null;
  172. }