【问题标题】:Flutter build called multiple times多次调用 Flutter 构建
【发布时间】:2021-03-05 17:43:32
【问题描述】:

我有一个底部导航栏,其中有一个列表页面,该页面使用 bloc 表示状态。

class _MainPageState extends State<MainPage> {
  int _index = 0;
  @override
  Widget build(BuildContext context) {
    final List<Widget> _widgets = [
      const ListPage(),
      Scaffold(),
      Scaffold(),
      
    ];

    return Scaffold(
        body: IndexedStack(
          index: _index,
          children: _widgets,
        ),
      bottomNavigationBar: BottomNavigationBar(
    ...


class ListPage extends StatelessWidget {
  const ListPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) =>
          getIt<ListBloc>()..add(const ListEvent.load(limit: 10)),
      child: SafeArea(
        child: Scaffold(
          appBar: AppBar(),
          body: const List(),
        ),
      ),
    );
  }
}

问题是build 被呼叫了 4 次。这会导致事件获取列表 4 次。

不知道在哪里添加事件以防止重新构建。

如果我在 statefull 小部件的 initState 中添加事件。当从小部件树中获取 bloc 时,Bloc 无法识别上下文中的 ListBloc。

【问题讨论】:

标签: flutter state bloc


【解决方案1】:

最好的方法是在主路由(包含底部导航的路由)的 initState 中添加事件。如下代码所示。

class _MainPageState extends State<MainPage> {
  int _index = 0;

  @override
  void initState() {
    super.initState();
    getIt<ListBloc>().add(const ListEvent.load(limit: 10));
  }

  @override
  Widget build(BuildContext context) {
    final List<Widget> _widgets = [
      const ListPage(),
      Scaffold(),
      Scaffold(),
      
    ];

    return Scaffold(
        body: IndexedStack(
          index: _index,
          children: _widgets,
        ),
      bottomNavigationBar: BottomNavigationBar(
    ...


class ListPage extends StatelessWidget {
  const ListPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) =>
          getIt<ListBloc>(),
      child: SafeArea(
        child: Scaffold(
          appBar: AppBar(),
          body: const List(),
        ),
      ),
    );
  }
}

【讨论】:

  • 有了这个。 initState 被调用了两次。
  • 不,不会的。
  • 如果它解决了您的问题,请接受我的回答。
【解决方案2】:

根据官方文档,您可以将 StatefulWidget 类与 KeepAlive 一起使用(将孩子标记为需要保持活力,即使它位于惰性列表中,否则会删除它)。

在代码中:

class YourStatefulWidget extends StatefulWidget {

//add with AutomaticKeepAliveClientMixin to the state class

class _YourStatefulWidgetState extends State<YourStatefulWidget > with AutomaticKeepAliveClientMixin {

  //add  super.build(context) under the build method
  Widget build(BuildContext context) {
  super.build(context);

  return //your widget

   @override
   bool get wantKeepAlive => true;
  }
 }
}

或者您可以通过使用状态管理类中的布尔值进行逻辑固定,因为如果小部件重建,该值不会改变。

【讨论】:

    【解决方案3】:
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            
            primarySwatch: Colors.blue,
           
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: MyHomePage(title: 'Flutter Demo Home Page',con: context,), // passing context
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title, this.con}) : super(key: key); // receiving context
      final String title;
      
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
    
      void _incrementCounter() {
        setState(() {
     
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
     
        return Scaffold(
          appBar: AppBar(
     
            title: Text(widget.title),
          ),
          body: Center(
     
            child: Column(
     
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '$_counter',
                  style: Theme.of(widget.con).textTheme.headline4, // using received context
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    
     
    }
    

    将上下文从一个类传递到另一个类,而不是每次都创建一个新的上下文。上面的例子是一个默认的 Flutter 应用,当你创建一个新的 Flutter 项目时。查看 cmets passing contextreceiving contextusing received context 我认为这应该可以解决问题..

    【讨论】:

    • 您能解释一下为什么向下传递上下文会阻止子项的重建。或者就此而言,当我将 create 函数放在父级中时,在父级中重建它并不起作用。
    • flutter.dev/docs/perf/rendering/best-practices .. Generally speaking, there are 2 uses cases for context : 1. Interact with your parents (get/post data mostly) 2. Once rendered on screen, get your screen size and position. The second point is kinda rare. On the other hand, the first point is used nearly everywhere.
    【解决方案4】:

    我认为问题出在MainPage。当您更改底部导航栏中的选定页面时,您会强制调用buildMainPage,但它每次都会重新创建_widgets 变量,这会强制更新子小部件。在类范围内移动final _widgets = ...,即您的变量将被创建一次。

    您还应该将provide 放在底部导航栏项目上方的级别,即用BlocProvider 包裹ListPage。而且它会在树中找到。或者尝试在ListPage 中的BlocProviderSafeArea 之间使用Builder

    要获得ListBloc,您需要在需要组合的小部件中使用BlocProvider.of&lt;ListBloc&gt;(context)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-30
      • 1970-01-01
      • 2020-10-18
      • 2021-08-13
      • 2021-02-17
      • 2021-08-04
      • 1970-01-01
      相关资源
      最近更新 更多