|
| 1 | +--- |
| 2 | +title: Pages |
| 3 | +slug: /generated-code/page-model |
| 4 | +sidebar_position: 4 |
| 5 | +--- |
| 6 | + |
| 7 | +# Generated Code: Pages |
| 8 | + |
| 9 | +When you create a new page in FlutterFlow, it automatically generates two files: a `Widget` class and a `Model` class. So if the name of the page you created is called ProductListPage, FlutterFlow generation backend will automatically create ProductListPageWidget class and ProductListPageModel class. |
| 10 | + |
| 11 | +:::info[Prerequisites] |
| 12 | +This guide uses example of the generated code of the **[EcommerceFlow demo app](https://bit.ly/ff-docs-demo-v1)**. To view the generated code directly, check out the **[Github repository](https://github.com/FlutterFlow/sample-apps/tree/main/ecommerce_flow)**. |
| 13 | +::: |
| 14 | + |
| 15 | +## PageModel class |
| 16 | + |
| 17 | + The `PageModel` classes are responsible for managing the state of individual pages and initializing the components used in these Pages. These classes extend the `FlutterFlowModel` class, which provides a consistent structure and shared functionality across all page models. |
| 18 | + |
| 19 | +#### Managing Local State |
| 20 | + |
| 21 | +A `PageModel` class typically holds local state fields specific to the page, which correspond to the **[Page State variables](../resources/ui/pages/page-lifecycle.md#page-state)**. |
| 22 | + |
| 23 | +For example, in the ProductListPage, user may create a Page State variable called `searchString`. Correspondingly, in the `product_list_page_model.dart` [file](https://github.com/FlutterFlow/sample-apps/blob/main/ecommerce_flow/lib/product/product_list_page/product_list_page_model.dart) (which is the `Model` file for the `ProductListPage`), the corresponding state field would be `_searchString`. This private field stores the current search string and includes a getter and setter to manage its value while logging any changes. |
| 24 | + |
| 25 | +```js |
| 26 | +String? _searchString; |
| 27 | +set searchString(String? value) { |
| 28 | + _searchString = value; |
| 29 | + debugLogWidgetClass(rootModel); |
| 30 | +} |
| 31 | +String? get searchString => _searchString; |
| 32 | +``` |
| 33 | +
|
| 34 | +:::tip[Private variables in Dart] |
| 35 | +In Dart, variables that start with an underscore (`_`), such as `_searchString`, are private to the class. This means they cannot be accessed outside the class or its scope. |
| 36 | +::: |
| 37 | +
|
| 38 | +In addition to managing local state, the given `PageModel` class also contains fields for handling the state of widgets on the page. For instance, `_dropDownValue` is a private field that stores the current value of a dropdown widget (if it is added to the current Page). Similar to `_searchString`, it has a getter and setter that logs changes to this field. |
| 39 | +
|
| 40 | +```js |
| 41 | +String? _dropDownValue; |
| 42 | +set dropDownValue(String? value) { |
| 43 | + _dropDownValue = value; |
| 44 | + debugLogWidgetClass(rootModel); |
| 45 | +} |
| 46 | +String? get dropDownValue => _dropDownValue; |
| 47 | +``` |
| 48 | +
|
| 49 | +#### Initializing child component models |
| 50 | +The `PageModel` class is also responsible for initializing the models of components used on the page. For example, if the page includes a `CartCounter` component, the model for this component is initialized within the page's model class. |
| 51 | +
|
| 52 | +```js |
| 53 | +// Model for CartCounter component. |
| 54 | + late CartCounterModel cartCounterModel; |
| 55 | + |
| 56 | +@override |
| 57 | +void initState(BuildContext context) { |
| 58 | + cartCounterModel = createModel(context, () => CartCounterModel()..parentModel = this); |
| 59 | + |
| 60 | +} |
| 61 | +``` |
| 62 | +:::info |
| 63 | +Only the model class of a child component is initialized inside the page or parent model class. In the case of page model classes, they are initialized within the widget’s state class itself. See the **[Widget class section](#pagewidget-class)** for more details. |
| 64 | +::: |
| 65 | +
|
| 66 | +When dealing with dynamic lists of components, such as those in a `ListView`, Row, or Column widget, the `PageModel` initializes a `Map<String, FlutterFlowModel>` to manage the state of each component instance. For example, if the page includes a list of `CategoryAvatar` components, the initialization might look like this: |
| 67 | +
|
| 68 | +```js |
| 69 | +// Models for CategoryAvatar dynamic component. |
| 70 | + Map<String, FlutterFlowModel> categoryAvatarModels = {}; |
| 71 | + |
| 72 | +``` |
| 73 | +
|
| 74 | +#### dispose() |
| 75 | +
|
| 76 | +Finally, the `dispose` function in the `ProductListPageModel` class is used to clean up resources when they are no longer needed. This is a common practice in Flutter to prevent memory leaks. In this class, the `dispose` function is overridden to dispose of the `cartCounterModel`, `searchQueryFocusNode`, and `searchQueryTextController`. |
| 77 | +
|
| 78 | +```js |
| 79 | + |
| 80 | + @override |
| 81 | + void dispose() { |
| 82 | + cartCounterModel.dispose(); |
| 83 | + searchQueryFocusNode?.dispose(); |
| 84 | + searchQueryTextController?.dispose(); |
| 85 | + } |
| 86 | +``` |
| 87 | +
|
| 88 | +
|
| 89 | +## PageWidget class |
| 90 | +
|
| 91 | +The `PageWidget` classes are responsible for creating the UI of individual pages and holding the widget tree as designed in the FlutterFlow canvas. These classes always extend Flutter's `StatefulWidget` class utilizing Flutter's built-in state management through `setState` to handle dynamic updates and interact with the app's lifecycle. |
| 92 | +
|
| 93 | +```js |
| 94 | +class OrderListPageWidget extends StatefulWidget { |
| 95 | + const OrderListPageWidget({ |
| 96 | + super.key, |
| 97 | + … |
| 98 | + }); |
| 99 | + |
| 100 | + final int? productId; |
| 101 | + … |
| 102 | + |
| 103 | + @override |
| 104 | + State<OrderListPageWidget> createState() => |
| 105 | + _OrderListPageWidgetState(); |
| 106 | +} |
| 107 | + |
| 108 | +``` |
| 109 | +
|
| 110 | +#### Route Awareness |
| 111 | +In the generated code, FlutterFlow automatically includes the `RouteAware` mixin in the **State** class. This makes the page aware of changes in the navigator's session history, allowing it to handle lifecycle events such as when the page becomes visible again after being removed. |
| 112 | +
|
| 113 | +```js |
| 114 | +class _OrderListPageWidgetState extends State<OrderListPageWidget> |
| 115 | + with RouteAware { |
| 116 | +``` |
| 117 | +
|
| 118 | +#### PageModel Initialization |
| 119 | +Additionally, the `PageModel` class is initialized within the state class. This class serves as a centralized place to manage the page’s state, handle business logic, and interact with the data layer. |
| 120 | +
|
| 121 | +```js |
| 122 | +class _OrderListPageWidgetState extends State<OrderListPageWidget> |
| 123 | + with RouteAware { |
| 124 | + late OrderListPageModel _model; |
| 125 | +``` |
| 126 | +
|
| 127 | +#### Global Scaffold Key |
| 128 | +Each page includes a `GlobalKey` for the `Scaffold`, which can be used to manage the scaffold's state, such as opening or closing drawers or snackbars programmatically. |
| 129 | +
|
| 130 | +```js |
| 131 | + final scaffoldKey = GlobalKey<ScaffoldState>(); |
| 132 | + |
| 133 | +return Scaffold( |
| 134 | + key: scaffoldKey, |
| 135 | + ...) |
| 136 | +``` |
| 137 | +
|
| 138 | +#### Keyboard Dismissal |
| 139 | +Moreover, the root widget of every page is a `GestureDetector` with an `onTap` callback that unfocuses the current input field. This ensures that any active keyboard is dismissed when tapping outside an input field, improving the user experience across pages. |
| 140 | +
|
| 141 | +```js |
| 142 | +return GestureDetector( |
| 143 | + onTap: () => FocusScope.of(context).unfocus(), |
| 144 | + child: Scaffold( |
| 145 | + ...) |
| 146 | +``` |
| 147 | +
|
| 148 | +These functionalities are automatically added by FlutterFlow to ensure seamless navigation and proper keyboard handling across pages. |
| 149 | +
|
0 commit comments