颤振:即使使用了`provider`,小部件仍在重建

Posted

技术标签:

【中文标题】颤振:即使使用了`provider`,小部件仍在重建【英文标题】:flutter: Widgets are rebuilding even though `provider` is used 【发布时间】:2020-06-14 15:48:09 【问题描述】:

我正在使用provider 进行颤振状态管理。下面是我的代码

首页

class HomePage extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        appBar: AppBar(
          //backgroundColor: Color.fromRGBO(0, 72, 100, 10),
          backgroundColor: Color.fromRGBO(25, 72, 114, 10),
          title: Text("something"),
          bottom: TabBar(
            indicatorColor: Colors.white70,
            labelStyle: TextStyle(
                fontFamily: 'Roboto-Regular',
                fontSize: 16.0,
                letterSpacing: 0.15),
            labelColor: Colors.white,
            labelPadding: EdgeInsets.all(5),
            tabs: <Widget>[
              Tab(
                text: "Fresh Products",
              ),
              Tab(
                text: "Frozen Products",
              ),
            ],
          ),
        ),
        body: Center(child: Home()),
      ),
    );
  


class Home extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    print("Home build methof");
    return TabBarView(
      children: <Widget>[
        Container(
          height: double.infinity,
          child: FutureBuilder(
            future: Provider.of<ProductSpeciesImpl>(context, listen: false)
                .getAllSpecies(),
            builder: (BuildContext context, AsyncSnapshot snapshot) 
              if (snapshot.connectionState == ConnectionState.waiting) 
                return Center(
                  child: Container(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        CircularProgressIndicator(),
                        Container(
                          height: 10,
                        ),
                        Text(
                          "Loading Data... Please Wait",
                          style: Theme.of(context).textTheme.body1,
                        )
                      ],
                    ),
                  ),
                );
               else 
                if (snapshot.hasError) 
                  return Center(
                    child: Text("An error Occured"),
                  );
                 else 
                  return Consumer<ProductSpeciesImpl>(
                      builder: (context, data, child) => GridView.builder(
                          physics:
                              ScrollPhysics(), // to disable GridView's scrolling
                          shrinkWrap: true,
                          itemCount: Provider.of<ProductSpeciesImpl>(context)
                              .productList
                              .length,
                          gridDelegate:
                              new SliverGridDelegateWithFixedCrossAxisCount(
                                  childAspectRatio:
                                      (MediaQuery.of(context).size.width *
                                          .5 /
                                          190),
                                  crossAxisCount: 2),
                          itemBuilder: (BuildContext context, int index) 
                            return GestureDetector(
                              onTap: () 
                                //print("sdsdsdsd");
                                // print(snapshot.data[index].name + " " + snapshot.data[index].idProductSpecies.toString() + " "+ snapshot.data[index].photo);
                                Navigator.pushNamed(context, "/products",
                                    arguments: 
                                      "name": Provider.of<ProductSpeciesImpl>(
                                              context,
                                              listen: false)
                                          .productList[index]
                                          .name,
                                      "id": Provider.of<ProductSpeciesImpl>(
                                              context,
                                              listen: false)
                                          .productList[index]
                                          .idProductSpecies,
                                      "photo": Provider.of<ProductSpeciesImpl>(
                                              context,
                                              listen: false)
                                          .productList[index]
                                          .photo
                                    );
                              ,
                              child: Card(
                                elevation: 4.0,
                                shape: RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(8.0)),
                                clipBehavior: Clip.antiAlias,
                                child: Column(
                                  mainAxisAlignment: MainAxisAlignment.start,
                                  children: <Widget>[
                                    Container(
                                      child: Text(
                                        Provider.of<ProductSpeciesImpl>(context,
                                                listen: false)
                                            .productList[index]
                                            .name,
                                      ),
                                    )
                                  ],
                                ),
                              ),
                            );
                          ));
                
              
            ,
          ),
        ),

ProductSpeciesImpl

class ProductSpeciesImpl
    with ChangeNotifier
    implements ProductSpeciesInterface 
  NavLinks navLinks = NavLinks();

  List<ProductSpecies> productList = [];

  @override
  Future<void> getAllSpecies() async 
    var data = await http.get(navLinks.getAllProductSpecies());
    var jsonData = convert.json.decode(data.body).cast<Map<String, dynamic>>();

    try 
      productList = jsonData
          .map<ProductSpecies>((json) => new ProductSpecies.fromJson(json))
          .toList();

      print("Product sIZE: " + productList.length.toString());

      notifyListeners();
     catch (error) 
      throw error;
    
  

代码运行良好。问题是每次我访问另一个页面并返回此页面时,UI 都会重新加载。我用过consumer,据我了解,consumer只会在调用时加载相关部分。这意味着我也不必在init 中运行我的产品加载代码。所以,我不明白为什么会这样。

感谢您对解决此问题的支持。

【问题讨论】:

【参考方案1】:

您正在通过调用getAllSpecies() 在您的build() 函数中发起HTTP 请求。你不应该那样做。

class Home extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    //...
          child: FutureBuilder(
            future: Provider.of<ProductSpeciesImpl>(context, listen: false)
                .getAllSpecies(),

build() 函数应该没有副作用。请将小部件转换为StatefulWidget 并在initState() 中加载。或者,让父 StatefulWidget 在其 initState() 中进行加载,并将数据传递给其构造函数中的此小部件。

【讨论】:

谢谢。我使用这种方式的原因是,在一个著名的教程中,他正在使用 futurebuilder 加载构建器,但令人惊讶的是没有重新加载!请您帮我理解这一点,consumerprovider 在页面重新加载时无法停止重建过程? 如果在调用getAllSpecies() 时只返回Provider 中的现有数据会更好。一般来说,你应该可以处理很多 build() 调用,你的代码不应该依赖于 build() 是否被调用。 好的。所以我将在initState 期间从服务器获取数据,并使用consumer 将这些“现有”数据加载到ListView。希望那很好? errrr.. 我转换为有状态的,并注意到我的initstate 也会在您离开屏幕并返回时运行。实际上,我在标签上,在标签之间导航, 我猜你可以在你的提供者类中做if (productList != null) loadProductList(),但这不是一个好方法。您的ProductSpeciesImpl 始终在加载,而从未使用过productList。如果我是你,我会放弃这个并使用pub.dev/documentation/provider/latest/provider/… 不要试图将 HTTP 加载与小部件事件联系起来。您的选项卡可以构建多次,您应该明确说明何时执行 HTTP 请求。这可能在包含选项卡的小部件的 initState 中。很抱歉之前的误导性投票。

以上是关于颤振:即使使用了`provider`,小部件仍在重建的主要内容,如果未能解决你的问题,请参考以下文章

通过 addPostFrameCallback 访问 Flutter Provider 时说小部件在小部件树之外,但颤振检查器显示其他情况

选择不依赖于状态的正确颤振块小部件

使 API 在所有小部件中都可访问 - 使用已实例化变量的 Provider?

如何在颤振应用程序之上覆盖小部件?

我的有状态小部件不会更新 ui,即使我正在调用 setState 并将正确的值传递给小部件类

如何在颤振小部件中使用条件语句/三元