【问题标题】:Flutter: setState() or markNeedsBuild() called when widget tree was locked... during orientation changeFlutter:当小部件树被锁定时调用 setState() 或 markNeedsBuild() ......在方向更改期间
【发布时间】:2018-06-28 19:30:12
【问题描述】:

我想创建一个可以根据设备方向更新其 UI 的应用程序。在 Portait 布局中,我使用抽屉来显示一些元素 (Fig.)。在横向布局中,没有抽屉,但元素显示在屏幕左侧的列中 (Fig.)。当抽屉关闭时,程序运行。

但是当我在抽屉打开的情况下将方向从纵向切换到横向时,出现“当小部件树被锁定时调用 setState() 或 markNeedsBuild()”的错误 堆栈提供以下信息:

Built build\app\outputs\apk\debug\app-debug.apk.
I/FlutterActivityDelegate(28527): onResume setting current activity to this
I/flutter (28527): PORTRAIT LAYOUT!!!!!!!!!!!!!!!!!!!!!!
I/hwaps   (28527): JNI_OnLoad
I/flutter (28527): LANDSCAPE LAYOUT!!!!!!!!!!!!!!!!!!!!!!
I/flutter (28527): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (28527): The following assertion was thrown while finalizing the widget tree:
I/flutter (28527): setState() or markNeedsBuild() called when widget tree was locked.
I/flutter (28527): This _ModalScope<dynamic> widget cannot be marked as needing to build because the framework is
I/flutter (28527): locked.
I/flutter (28527): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (28527):   _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#e1f0f](state:
I/flutter (28527):   _ModalScopeState<dynamic>#830e3)
I/flutter (28527):
I/flutter (28527): When the exception was thrown, this was the stack:
I/flutter (28527): #0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3453:9)
I/flutter (28527): #1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3462:6)
I/flutter (28527): #2      State.setState (package:flutter/src/widgets/framework.dart:1141:14)
I/flutter (28527): #3      _ModalScopeState._routeSetState (package:flutter/src/widgets/routes.dart:464:5)
I/flutter (28527): #4      ModalRoute.setState (package:flutter/src/widgets/routes.dart:562:30)
I/flutter (28527): #5      ModalRoute.changedInternalState (package:flutter/src/widgets/routes.dart:1018:5)
I/flutter (28527): #6      _ModalRoute&TransitionRoute&LocalHistoryRoute.removeLocalHistoryEntry (package:flutter/src/widgets/routes.dart:348:7)
I/flutter (28527): #7      LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:296:12)
I/flutter (28527): #8      DrawerControllerState.dispose (package:flutter/src/material/drawer.dart:201:20)
I/flutter (28527): #9      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:3821:12)
I/flutter (28527): #10     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1697:13)
I/flutter (28527): #11     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
I/flutter (28527): #12     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3676:14)
I/flutter (28527): #13     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
I/flutter (28527): #14     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
I/flutter (28527): #15     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3676:14)
I/flutter (28527): #16     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
I/flutter (28527): #17     ListIterable.forEach (dart:_internal/iterable.dart:39:13)
I/flutter (28527): #18     _InactiveElements._unmountAll (package:flutter/src/widgets/framework.dart:1706:25)
I/flutter (28527): #19     BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:2328:27)
I/flutter (28527): #20     BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2160:15)
I/flutter (28527): #21     BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:2327:7)
I/flutter (28527): #22     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:628:18)
I/flutter (28527): #23     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:208:5)
I/flutter (28527): #24     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter (28527): #25     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter (28527): #26     _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
I/flutter (28527): #27     _invoke (dart:ui/hooks.dart:120:13)
I/flutter (28527): #28     _drawFrame (dart:ui/hooks.dart:109:3)
I/flutter (28527): ════════════════════════════════════════════════════════════════════════════════════════════════════
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.
I/flutter (28527): Another exception was thrown: 'package:flutter/src/widgets/framework.dart': Failed assertion: line 3424 pos 12: '_debugLifecycleState != _ElementLifecycle.defunct': is not true.

我在 Appbar (Fig.) 的前面有一个返回按钮。

这是我的代码:

import 'package:flutter/material.dart';

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

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'demo',
      theme: new ThemeData(primarySwatch: Colors.blue),
      home: new Home(),
    );
  }
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  static const int SCREEN_WIDTH_MIN = 600;

  @override
  Widget build(BuildContext context) {
    Widget _homelayout;
    var screenWidth = MediaQuery.of(context).size.width;
    var screenOrientation = MediaQuery.of(context).orientation;
    if ((screenWidth > SCREEN_WIDTH_MIN) &&
        (screenOrientation == Orientation.landscape)) {
      print('LANDSCAPE LAYOUT!!!!!!!!!!!!!!!!!!!!!!');
      //_homelayout = _buildLandscapeLayout(context);
      _homelayout = _buildLandscapeLayout();
    } else {
      print('PORTRAIT LAYOUT!!!!!!!!!!!!!!!!!!!!!!');
      _homelayout = _buildPortraitLayout();
    }
    return _homelayout;
  }

  Widget _buildPortraitLayout() {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('demo'),
      ),
      drawer: new DeviceListDrawer(),
      body: new Center(
        child: new Text('body'),
      ),
    );
  }
}

Widget _buildLandscapeLayout() {
  return new Scaffold(
    appBar: new AppBar(
      title: new Text('demo'),
    ),
    body: new Center(
      child: new Row(
        children: <Widget>[
          new DeviceListDrawer(),
          new Text('body')
        ],
      ),
    ),
  );
}

class DeviceListDrawer extends StatefulWidget {
  @override
  _DeviceListDrawerState createState() => _DeviceListDrawerState();
}

class _DeviceListDrawerState extends State<DeviceListDrawer> {
  @override
  Widget build(BuildContext context) {
    return new Container(
      width: 200.0,
      color: Colors.white,
      child: Column(
        children: <Widget>[
          new Container(
            width: 200.0,
            color: Colors.blue,
            padding: new EdgeInsets.only(
                top: MediaQuery.of(context).padding.top + 16.0, bottom: 16.0),
            child: new Text('demo '),
          ),
          new Expanded(
            child: new ListView(
              children: <Widget>[
                new Text('item 1'),
                new Text('item 2'),
                new Text('item 3'),
              ],
            ),
          ),
          new RaisedButton(
            child: new Text('add item'),
            onPressed: () {},
          )
        ],
      ),
    );
  }
}

你能指出是什么问题吗?以及如何使布局充满活力?感谢您的帮助。

【问题讨论】:

  • 这通常发生在您已经在新视图中(在您的情况下为横向模式)并在已经消失的纵向模式上调用 setState 时。在进入横向模式之前,您应该以编程方式关闭抽屉。
  • 我同意你的观点。我试图在 _HomeState 的构建方法开始时使用 Navigator.pop() 关闭抽屉,但它不起作用。我知道我应该在哪里关闭抽屉吗?谢谢

标签: android dart flutter drawer flutter-layout


【解决方案1】:

你可以试试

Navigator.pop(context, true);

而不是

Navigator.pop()

【讨论】:

    【解决方案2】:
    void rebuild(BuildContext context) {
      Navigator.pop(context);
      setState(() {});
    }
    

    【讨论】:

      【解决方案3】:

      对于遇到这种情况的新人,您可以做的一件事就是在抽屉打开时将设备方向锁定为纵向模式。这样可以防止该错误。

      在我的例子中,我根据屏幕宽度(显然会随着方向而变化)构建应用栏和抽屉,并且像 OP 一样,我只构建窄屏幕的抽屉(在较小的设备上以纵向模式出现)。

      return Scaffold(
        appBar: _buildAppBar(context),
        drawer: _buildDrawer(context),
        body: Beamer(
          key: _beamerKey,
          routerDelegate: routerDelegate,
        ),
        onDrawerChanged: (isOpened) {
          // this is a workaround to fix an error that happens if user changes
          // device orientation while drawer is open.  Basically, we lock it
          // in portrait mode when the drawer is open so it can't be rebuilt
          // without the drawer by rotating the device.
          if (isOpened) {
            // lock orientation to portrait mode while drawer is open
            SystemChrome.setPreferredOrientations(
                [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
          } else {
            // closed, unlock
            // lock orientation to portrait mode while drawer is open
            SystemChrome.setPreferredOrientations([
              DeviceOrientation.portraitUp,
              DeviceOrientation.portraitDown,
              DeviceOrientation.landscapeLeft,
              DeviceOrientation.landscapeRight
            ]);
          }
        },
      );
      

      【讨论】:

        猜你喜欢
        • 2018-01-06
        • 2021-09-16
        • 1970-01-01
        • 2020-12-12
        • 2020-12-28
        • 2021-01-06
        • 2021-05-31
        • 2021-08-24
        相关资源
        最近更新 更多