От StatefulWidget перейдите к StatelessWidget, который запускает последний, но также и первый - PullRequest
0 голосов
/ 30 сентября 2018

Есть ли способ предотвратить многократный вызов build()?

Следующий код создает две страницы, каждая с одной кнопкой, кнопка на FirstPage переходит на SecondPage итот, что на SecondsPage возвращается к FirstPage.

Проблема в том, что если вы нажмете кнопку на 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 ----------

Спасибо за помощь.

EDIT:

Я пытался использовать 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;
            });
        },
        ...

Это плохая практика или можетсоздать какие-либо проблемы?

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

Flutter всегда строит каждую «страницу» (виджет) в стеке навигации после каждого нажатия / нажатия.Это нормальное и предполагаемое поведение, так что каждый виджет может обновлять себя при необходимости.

Методы сборки виджета должны быть идемпотентными, хотя обычно он строится только тогда, когда вы думаете, что фреймворк может решить в любой момент создать виджет по любой причине - вы не можете полагаться на любой виджетстроительство.

Обычно это не проблема, Flutter сравнивает все новые виджеты со всеми старыми виджетами в сборке, если виджеты идентичны, они не будут заменены или затронуты каким-либо другим образом.

Короткий ответ, таким образом, «Нет», конечно, если вы хотите, чтобы вы могли обойти это (например, переопределив навигатор), вы определенно можете.

Вы можете просмотреть код, связанный с созданием дочерних элементов виджета (включая навигатор) здесь: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/framework.dart

Но в идеале вы просто хотите предположить, что что-либо может быть создано в любое время - так каконо может.Flutter не дает никаких обещаний относительно того, когда он будет (или не будет) создавать ваши виджеты, любой код, который не написан с учетом этого, может сломаться!

См. Эту проблему: https://github.com/flutter/flutter/issues/11655

0 голосов
/ 30 сентября 2018

это только потому, что виджет все еще находится в стеке маршрутов.я думаю, что флаттер перерисовывает виджеты, которые находятся в стеке.если вы откроете FirstPage перед маршрутизацией на SecondPage, он не будет отображать сообщение первого виджета.

РЕДАКТИРОВАТЬ:

как вы можете использовать.

onPressed: () {
    Navigator.pop(context);
    Navigator.push(
       context,
       MaterialPageRoute(builder: (context) => SecondPage()),
     );
},
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...