Slide 1

Slide 1 text

State Management Introduction By Ali Yazdi [email protected] linkedin.com/in/aliyazdi75

Slide 2

Slide 2 text

Contents ● Widget states ● InheritedWidget ● State management ● ChangeNotifier ● Provider ● Design pattern or Architecture

Slide 3

Slide 3 text

Thinking declaratively // Imperative style b.setColor(red) b.clearChildren() ViewC c3 = new ViewC(...) b.add(c3) // Declarative style return ViewB( color: red, child: const ViewC(), ); In order to lighten the burden on developers from having to program how to transition between various UI states, Flutter, by contrast, lets the developer describe the current UI state and leaves the transitioning to the framework like opacity animation.

Slide 4

Slide 4 text

Widget state: stateless ● Suitable for immutable state ● The build method of a stateless widget is typically only called in three situations: ○ the first time the widget is inserted in the tree ○ when the widget's parent changes its configuration ○ when an InheritedWidget it depends on changes ● Sub widgets can change like Stream widget ● If the 2nd and 3rd happen frequently, isolate the widget to optimize the performance class Frog extends StatelessWidget { const Frog({ Key? key, this.color = const Color(0xFF2DBD3A), this.child, }) : super(key: key); final Color color; final Widget? child; @override Widget build(BuildContext context) { return Container(color: color, child: child); } }

Slide 5

Slide 5 text

Widget state: stateful ● Suitable for mutable state ● State is information that: ○ Can be read synchronously when the widget is built ○ Might change during the lifetime of the widget ● Notify state changes using State.setState ● Calling setState notifies the framework that the internal state of this object has changed, and it rebuilds the whole widget ● It is an error to call setState in build method or after the framework calls dispose, so use if(mounted) if you use it in async function

Slide 6

Slide 6 text

Performance considerations ● Widgets that use State.setState or depend on InheritedWidgets. These will typically rebuild many times during the application's lifetime ● Push the state to the leaves ● If a subtree does not change, cache the widget (By final or const) that represents that subtree and re-use it each time it can be used ● Avoid changing the depth of any created subtrees or changing the type of any widgets in the subtree ● In general, avoid using stateful widget except for ui only use-case like animations or app lifecycle Bad Structure Example Vs Better Structure Example

Slide 7

Slide 7 text

InheritedWidget ● Base class for widgets that efficiently propagate information down the tree, like Theme.of(context) ● To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.dependOnInheritedWid getOfExactType. ● Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state

Slide 8

Slide 8 text

InheritedWidget class StudentState extends InheritedWidget { const StudentState({ Key? key, required this.grade, required Widget child, }) : super(key: key, child: child); final int grade; static StudentState of(BuildContext context) { final StudentState? result = context.dependOnInheritedWidgetOfExactType(); assert(result != null, 'No StudentStatefound in context'); return result!; } @override bool updateShouldNotify(StudentState old) => grade != old.grade; } final studentState = StudentState.of(context);

Slide 9

Slide 9 text

Performance considerations ● Widget 1 changes its state on InheritedWidget frequently ● Widget 2 is not dependent on the state of its parent InheritedWidget ● So widget 2 always rebuilds itself because of widget 1 ● Isolate widget 1 and its state from the other widgets

Slide 10

Slide 10 text

State management There comes a time when you need to share application state between screens, across your app. There are many approaches you can take, and many questions to think about.

Slide 11

Slide 11 text

E ephemeral state Vs A pp state ● Ephemeral state (sometimes called UI state or local state) is the state you can neatly contain in a single widget like: ○ current page in a PageView ○ current progress of a complex animation ○ current selected tab in a BottomNavigationBar ● State that is not ephemeral, that you want to share across many parts of your app, and that you want to keep between user sessions, is what we call application state like the shopping cart in an e-commerce app ● There are two conceptual types of state in any Flutter app: ○ Ephemeral state can be implemented using State and setState(), and is often local to a single widget ○ The rest is your app state

Slide 12

Slide 12 text

ChangeNotifier A simple and straightforward class included in the flutter sdk to manage states in the application

Slide 13

Slide 13 text

ChangeNotifier The shopping cart in an e-commerce app: ● Add product ● Remove product ● Checkout the cart

Slide 14

Slide 14 text

ChangeNotifier class CartState extends ChangeNotifier { /// Internal, private state of the cart. final List _items = []; /// An unmodifiable view of the items in the cart. UnmodifiableListView get items => UnmodifiableListView(_items); /// The current total price of all items (assuming all items cost $42). int get totalPrice => _items.length * 42; /// Adds [item] to cart. This and [removeAll] are the only ways to modify the /// cart from the outside. void add(Item item) { _items.add(item); // This call tells the widgets that are listening to this model to rebuild. notifyListeners(); } /// Removes all items from the cart. void removeAll() { _items.clear(); // This call tells the widgets that are listening to this model to rebuild. notifyListeners(); } }

Slide 15

Slide 15 text

ChangeNotifier class CartStateScope extends InheritedNotifier { const CartStateScope({ super.key, super.notifier, required super.child, }); static CartState of(BuildContext context) { final result = context.dependOnInheritedWidgetOfExactType()?.notifier; assert(result != null, 'No CartStateScope found in context'); return result!; } } CartStateScope.of(context).add(Item(name: 'Phone')); CartStateScope.of(context).removeAll();

Slide 16

Slide 16 text

ChangeNotifier class MyCart extends StatefulWidget { const MyCart({Key? key}) : super(key: key); @override _MyCartState createState() => _MyCartState(); } class MyCartState extends State with ChangeNotifier { late CartState cartState; @override void initState() { cartState = CartState()..addListener(notifyListeners); super.dispose(); } @override void dispose() { cartState.removeListener(notifyListeners); super.dispose(); } @override Widget build(BuildContext context) { final totalPrice = CartStateScope.of(context).totalPrice; return Text(totalPrice); } }

Slide 17

Slide 17 text

ChangeNotifier Advantages: ● Simple and easy to use within the flutter framework ● Suitable for the simple and small features like theming, navigation, fonts, localization Disadvantages: ● Using StatefulWidget ● Boilerplate of writing each InheritedWidget and adding and removing listeners

Slide 18

Slide 18 text

Solution Using dedicated packages for the state managements like Provider: ● A wrapper around InheritedWidget to make them easier to use and more reusable. ● Simplified allocation/disposal of resources ● A vastly reduced boilerplate over making a new class every time ● But it’s hard to implement a design pattern or architecture

Slide 19

Slide 19 text

Provider class MyCart extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer( builder: (context, cart, child) => Stack( children: [ // Use SomeExpensiveWidget here, without rebuilding every time. if (child != null) child, Text("Total price: ${cart.totalPrice}"), ], // Build the expensive widget here. child: SomeExpensiveWidget(), ), ); } } Provider.of(context, listen: false)!.add(Item(name: 'Phone')); Provider.of(context, listen: false)!.removeAll(); ChangeNotifier Example Vs Provider Example

Slide 20

Slide 20 text

Design pattern or Architecture The goal of using most of design patterns is to separate Data Model and View, why: ● Make a clean structure for beautiful and easy to read ● More comprehensible business logic ● Separate features to simultaneously programming ● Using reusable components ● High scalability ● Writing test for each component

Slide 21

Slide 21 text

List of state management approaches The norm is, no matter what you choose, business requirements should be in the priority, like: ● Speed ● Scalability ● Performance ● Stability ● Maintainability ● Quality ● Quantity

Slide 22

Slide 22 text

The Only Resource: flutter.dev

Slide 23

Slide 23 text

Thanks for your attention 💙 QA Email: [email protected] Linkedin: linkedin.com/in/aliyazdi75