从StatefulWidget导航到StatelessWidget,它运行最后一个构建器,但同时运行第一个构建器

时间:2018-09-29 21:50:28

标签: flutter

是否有一种方法可以防止多次调用build()

下一个代码创建两个页面,每个页面都有一个按钮,FirstPage上的按钮导航到SecondPage,而SecondsPage上的按钮返回到FirstPage。 / p>

问题在于,如果您单击FirstPage上的按钮,它将运行SecondPage build(),但也会运行FirstPage build()

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'First page',
      home: FirstPage(),
    );
  }
}

class FirstPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _FirstPageState();
  }
}

class _FirstPageState extends State<FirstPage> {
  @override
  Widget build(BuildContext context) {
    print('---------- In FirstPage build ----------');
    return Scaffold(
      appBar: AppBar(
        title: Text('First page'),
      ),
      body: Column(
        children: [
          Text('First page body'),
          FlatButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => SecondPage()),
              );
            },
            child: Text('Go to second page')
          )
        ]
      )
    );
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('---------- In SecondPage build ----------');
    return Scaffold(
      appBar: AppBar(
        title: Text('Second page'),
      ),
      body: Column(
        children: [
          Text('Second page body'),
          FlatButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: Text('Go back')
          )
        ]
      )
    );
  }
}

控制台输出:

Performing hot restart...                                        
D/dalvikvm(23908): threadid=12: interp stack at 0x76040000
Restarted app in 3,849ms.
I/flutter (23908): ---------- In FirstPage build ----------
I/SurfaceTextureClient(23908): [STC::queueBuffer] (this:0x76b39810) fps:0.07, dur:14731.13, max:14731.13, min:14731.13
I/SurfaceTextureClient(23908): [STC::queueBuffer] this:0x76b39810, api:1, last queue time elapsed:14731.13

点击按钮后:

V/Provider/Settings(23908): from db cache, name = sound_effects_enabled , value = 0
I/flutter (23908): ---------- In SecondPage build ----------
I/SurfaceTextureClient(23908): [STC::queueBuffer] (this:0x76b39810) fps:0.24, dur:4140.60, max:4140.60, min:4140.60
I/flutter (23908): ---------- In FirstPage build ----------

感谢您的帮助。

编辑:

我尝试在popAndPushNamed()上使用FirstPage,它只是在SecondPage上运行构建,但是如果我尝试返回:

Navigator.popAndPushNamed(
    context,
    '/'
);

我得到了错误:

I/flutter ( 5005): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter ( 5005): The following assertion was thrown while handling a gesture:
I/flutter ( 5005): Cannot reuse a MaterialPageRoute<dynamic> after disposing it.
I/flutter ( 5005): 'package:flutter/src/widgets/routes.dart': Failed assertion: line 215 pos 12:
I/flutter ( 5005): '!_transitionCompleter.isCompleted'
I/flutter ( 5005): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 5005): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 5005): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 5005):   https://github.com/flutter/flutter/issues/new
I/flutter ( 5005): When the exception was thrown, this was the stack:
I/flutter ( 5005): #2      TransitionRoute.didPopNext (package:flutter/src/widgets/routes.dart)
I/flutter ( 5005): #3      NavigatorState.pop (package:flutter/src/widgets/navigator.dart:1691:23)
I/flutter ( 5005): #4      NavigatorState.popAndPushNamed (package:flutter/src/widgets/navigator.dart:1399:5)
I/flutter ( 5005): #5      Navigator.popAndPushNamed (package:flutter/src/widgets/navigator.dart:731:34)
I/flutter ( 5005): #6      SecondPage.build.<anonymous closure> (file:///home/ncs/Documents/flutter/src/test1/lib/main.dart:83:25)
I/flutter ( 5005): #7      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:494:14)
I/flutter ( 5005): #8      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:549:30)
I/flutter ( 5005): #9      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:102:24)
I/flutter ( 5005): #10     TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:161:9)
I/flutter ( 5005): #11     TapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:94:7)
I/flutter ( 5005): #12     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:315:9)
I/flutter ( 5005): #13     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:73:12)
I/flutter ( 5005): #14     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:101:11)
I/flutter ( 5005): #15     _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:143:19)
I/flutter ( 5005): #16     _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:121:22)
I/flutter ( 5005): #17     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:101:7)
I/flutter ( 5005): #18     _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:64:7)
I/flutter ( 5005): #19     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:48:7)
I/flutter ( 5005): #20     _invoke1 (dart:ui/hooks.dart:134:13)
I/flutter ( 5005): #21     _dispatchPointerDataPacket (dart:ui/hooks.dart:91:5)
I/flutter ( 5005): (elided 2 frames from class _AssertionError)
I/flutter ( 5005): Handler: onTap
I/flutter ( 5005): Recognizer:
I/flutter ( 5005):   TapGestureRecognizer#94626(debugOwner: GestureDetector, state: possible, won arena, finalPosition:
I/flutter ( 5005):   Offset(65.3, 119.6), sent tap down)
I/flutter ( 5005): ════════════════════════════════════════════════════════════════════════════════════════════════════

我能够删除不想要的版本的唯一方法是通过以下条件::

class _FirstPageState extends State<FirstPage> {
    bool _canBuild = true;

    @override
    Widget build(BuildContext context) {
        if (!_canBuild) {
            return Container();
        }
        print('---------- In FirstPage build ----------');
        ...
        onPressed: () {
            _canBuild = false;
            Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => SecondPage()),
            ).then((_) {
                _canBuild = true;
            });
        },
        ...

这是不好的做法还是会造成任何问题?

3 个答案:

答案 0 :(得分:0)

这仅仅是因为小部件仍在路由栈中。我认为颤动会重新渲染堆栈中的小部件。如果在路由到SecondPage之前弹出FirstPage,它将不会显示第一个小部件的消息。

编辑:

如何使用。

pip install tensorflow

答案 1 :(得分:0)

Flutter总是在每次按下/弹出后在导航堆栈中构建每个“页面”(小部件)。这是正常的和预期的行为,因此每个窗口小部件都可以在需要时自行更新。

小部件的构建方法应该是幂等的,尽管通常仅在您认为会构建时才构建,但框架可能出于任何原因在任何时候决定构建小部件-您不能依赖任何未构建的小部件。

通常这实际上不是问题,Flutter会将所有新的窗口小部件与构建中的所有旧窗口小部件进行比较,如果窗口小部件相同,则它们将不会被替换或以任何其他方式受到影响。

因此,简短的答案是“否”,当然,如果您愿意,可以解决此问题(例如,通过覆盖导航器),那么您绝对可以。

您可以在此处查看与构建窗口小部件(包括导航器)的子代相关的代码:https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/framework.dart

但是理想情况下,您只是想假设任何东西都可以随时构建-可以。 Flutter不保证何时(或将不会)构建您的窗口小部件,任何未考虑到这一点的代码都容易被破坏!

看到此问题:https://github.com/flutter/flutter/issues/11655

答案 2 :(得分:-1)

我也面临着同样的问题。然后,我使用pushNamed()而不是popAndPushNamed()

Navigator.popAndPushNamed(context,'/');

我不知道原因,但是它在起作用!