setState() doesn't execute until the enclosing function returns

前端 未结 2 378
终归单人心
终归单人心 2021-01-24 21:16

So I was trying this code in flutter:

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyA         


        
相关标签:
2条回答
  • 2021-01-24 21:52

    If you want to run code after the widget is rebuild use:

    WidgetsBinding.instance.addPostFrameCallback((_) {
    //doStuffAfterNextBuild
    });
    

    The function passed to setState is run directly.

    "The provided callback is immediately called synchronously"
    

    setState only marks the widget as dirty.

    "Calling setState notifies the framework that the internal state of this object has changed"
    

    setState does not itself manipulate your state in any way.

    (Quotes from https://api.flutter.dev/flutter/widgets/State/setState.html)


    See for example the following:

    class _MyWidgetState extends State<MyWidget> {
      String s = ""
    
      // ... Somewhere in the tree
      onPressed: () {
        // State is updated immediately with this call
        setState(() {
          this.s = "1";
          // Implementation of setState calls
          // _element.markNeedsBuild();
        });
    
        // State is updated again
        this.s = "2";
      }
    }
    
    

    This will result in the states s beeing "2". Although the closure passed to setState sets s to "1", it is again modified after that in the closure of onPressed.

    You can actually update the state outside of setStates closure entirely:

    class _MyWidgetState extends State<MyWidget> {
      String s = ""
    
      // ... Somewhere in the tree
      onPressed: () {
        // State is updated immediately
        this.s = "1";
        setState(() {
          // Implementation of setState calls
          // _element.markNeedsBuild();
        });
      }
    }
    
    

    If you look at the implementation of setState you can see that it does not actually do anything with the callback besides of executing it and making sure you did not give it an asynchronous function and that the widget still exists:

    @protected
    void setState(VoidCallback fn) {
      assert(fn != null);
      assert(() {
        if (_debugLifecycleState == _StateLifecycle.defunct) {
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('setState() called after dispose(): $this'),
            ErrorDescription(
              'This error happens if you call setState() on a State object for a widget that '
              'no longer appears in the widget tree (e.g., whose parent widget no longer '
              'includes the widget in its build). This error can occur when code calls '
              'setState() from a timer or an animation callback.'
            ),
            ErrorHint(
              'The preferred solution is '
              'to cancel the timer or stop listening to the animation in the dispose() '
              'callback. Another solution is to check the "mounted" property of this '
              'object before calling setState() to ensure the object is still in the '
              'tree.'
            ),
            ErrorHint(
              'This error might indicate a memory leak if setState() is being called '
              'because another object is retaining a reference to this State object '
              'after it has been removed from the tree. To avoid memory leaks, '
              'consider breaking the reference to this object during dispose().'
            ),
          ]);
        }
        if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('setState() called in constructor: $this'),
            ErrorHint(
              'This happens when you call setState() on a State object for a widget that '
              "hasn't been inserted into the widget tree yet. It is not necessary to call "
              'setState() in the constructor, since the state is already assumed to be dirty '
              'when it is initially created.'
            ),
          ]);
        }
        return true;
      }());
      final dynamic result = fn() as dynamic;
      assert(() {
        if (result is Future) {
          throw FlutterError.fromParts(<DiagnosticsNode>[
            ErrorSummary('setState() callback argument returned a Future.'),
            ErrorDescription(
              'The setState() method on $this was called with a closure or method that '
              'returned a Future. Maybe it is marked as "async".'
            ),
            ErrorHint(
              'Instead of performing asynchronous work inside a call to setState(), first '
              'execute the work (without updating the widget state), and then synchronously '
             'update the state inside a call to setState().'
            ),
          ]);
        }
        // We ignore other types of return values so that you can do things like:
        //   setState(() => x = 3);
        return true;
      }());
      _element.markNeedsBuild();
    }
    

    (Also from https://api.flutter.dev/flutter/widgets/State/setState.html)


    Check out this great video for more insight into how State works in Flutter: https://www.youtube.com/watch?v=dkyY9WCGMi0





    P.S.

    Just for fun you could also handle the creation of the element for the StatefulWidget your self and mark it manually. Although you definitely should not

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage();
    
      late final StatefulElement element;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
      
      @override
      StatefulElement createElement() { 
        this.element = StatefulElement(this); 
        return element;
      }
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      String s = "";
    
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Text(
              '$s',
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              this.s = "2";
              widget.element.markNeedsBuild();
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    0 讨论(0)
  • 2021-01-24 22:14

    Event Loop

    • There is something called Event Loop

    • Event Loop process Events in order

    You have two events in order

    Event A => Click => by the User

        0.onPressed: () {
            1.setState(() {
                3. i++
                4. Mark as widget dirty
                5. Add to the global dirty widgets list
            });
            6.i++
        });
    

    Event B => Vsync signal => provided by the OS

        7. check dirty widgets list
        8. repaint
    

    More

    • Here a YouTube video from Flutter in Focus series

      • Isolates and Event Loops

        • by flutter team member Andrew Brogdon (@RedBrogdon)
    • Or read it here

      • Dart asynchronous programming: Isolates and event loops

    Ref.:

    1. setState method - State class - widgets library - Dart API

    2. markNeedsBuild method - Element class - widgets library - Dart API

    3. scheduleBuildFor method - BuildOwner class - widgets library - Dart API

    4. drawFrame method - WidgetsBinding class - widgets library - Dart API

    5. handleDrawFrame method - SchedulerBinding class - scheduler library - Dart API

    6. buildScope method - BuildOwner class - widgets library - Dart API

    7. dart engine loop - Google Search

    8. Dart Programming - Loops - Tutorialspoint

    9. optimization - What is the optimal render loop in Dart 2? - Stack Overflow

    10. Understanding Flutter Render Engine - Stack Overflow

    11. Technical overview - Flutter

    12. Flutter - Dart API docs

    13. flutter/spinning_square.dart at master · flutter/flutter

    14 .dart engine - Google Search

    1. scheduler library - Dart API

    2. flutter/binding.dart at master · flutter/flutter

    3. scheduler library - Dart API

    4. frame scheduling flutter - Google Search

    5. scheduleFrame method - SchedulerBinding class - scheduler library - Dart API

    6. scheduler library - Dart API

    7. packages/flutter/lib/scheduler.dart - external/github.com/flutter/flutter - Git at Google

    8. flutter/spinning_square.dart at master · flutter/flutter

    9. dart engine - Google Search

    10. threading | Dart Package

    11. isolate flutter - Google Search

    0 讨论(0)
提交回复
热议问题