简体   繁体   English

强制 navDrawer state 从外部更新 state scope

[英]Force navDrawer state update from outside state scope

I am currently developing an e-commerce mobile app.我目前正在开发一个电子商务移动应用程序。

Right now I am working on my navigation.现在我正在研究我的导航。 There's a bunch of categories that can have subcategories and the subcategories can have their own subcategories.有很多类别可以有子类别,而子类别可以有自己的子类别。 I retrieve a list of all categories via an API on app init and then I store it.我通过应用程序初始化上的 API 检索所有类别的列表,然后将其存储。

Here's an example of my dataset:这是我的数据集的示例:

{
      "id":"41490",
      "name":"Electrical Equipment",
      "subCategories":[
         {
            "id":"41492",
            "name":"Breakers",
            "subCategories":[
               {
                  "id":"167542",
                  "name":"1 Pole",
                  "subCategories":[
                     {
                        "id":"167577",
                        "name":"15 Amp",
                        "subCategories":null
                     },
                     {
                        "id":"167585",
                        "name":"20 Amp",
                        "subCategories":null
                     },
                     {
                        "id":"167600",
                        "name":"30 Amp",
                        "subCategories":null
                     },
                     {
                        "id":"167606",
                        "name":"40 Amp",
                        "subCategories":null
                     }
                  ]
               },

I am using a listview and a listview builder to make my category list.我正在使用 listview 和 listview builder 来制作我的类别列表。 The listview builder also calls a recursive function to make the subcategories.列表视图构建器还调用递归 function 来创建子类别。 I've managed to get everything to generate dynamically meaning that if on the website we add a bunch of categories then the app will update itself automatically via the API.我已经设法让所有内容动态生成,这意味着如果我们在网站上添加一堆类别,那么应用程序将通过 API 自动更新。

My problem now is that when I click my categories, the navDrawer doesn't redraw.我现在的问题是,当我单击我的类别时,navDrawer 不会重绘。 I have to close the categories and re-open them to make it redraw.我必须关闭类别并重新打开它们才能重绘。 I need some new concepts, been scratching my head on this one for a while.我需要一些新概念,在这个问题上摸索了一段时间。

I think there might be an issue with the structure of my code since I initialize the categories outside the state.我认为我的代码结构可能存在问题,因为我初始化了 state 之外的类别。

Here's my navDrawer class:这是我的 navDrawer class:

class navDrawer extends StatefulWidget {
  bool _expandCategories = false;
  bool _expandAccount = false;
  List _categories;
  var _categoryList;
  List _tempSubCats;

  void flickCategories(){
    //_expandCategories = !_expandCategories;
    //sleep(const Duration(microseconds: 100));
    //_expandCategories = !_expandCategories;
  }

  void setCategories(List categories){
      _categories = categories;
      int catCount = categories.length;
      _categoryList = new ListView.builder(
          //shrinkWrap: true,
          //physics: ClampingScrollPhysics(),
          padding:EdgeInsets.all(0.0),
          itemCount: catCount,
          itemBuilder: (BuildContext context, int index) => buildCategories(context, index),
      );
  }
  Widget buildCategories(BuildContext context, int index){
    if(_categories[index]['subCategories']!=null){
      if(idHandler.isIdOpen(_categories[index]['id'])){
        _tempSubCats = [];
        buildSubCategories(_categories[index],2);
        ListView subCategories = new ListView.builder(
            shrinkWrap: true,
            physics: NeverScrollableScrollPhysics(),
            itemCount: _tempSubCats.length,
            itemBuilder: (BuildContext ct, int i){
                return _tempSubCats[i];
            }
        );
        return Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              height: 30.0,
              child: ListTile(
                  title: Row(
                      children: [
                        Text("    " + _categories[index]['name']),
                        Transform.rotate(
                            angle: -math.pi/2,
                            child:
                            Transform.scale(
                                scale: 0.75,
                                child:
                                Icon(Icons.arrow_back)
                            )
                        )
                      ]
                  ),
                  onTap: () {
                    flickCategories();
                    idHandler.toggleId(_categories[index]['id']);
                  }
              ),
              padding: EdgeInsets.all(0.0),
              margin: EdgeInsets.all(0.0)
          ),
          MediaQuery.removePadding(
              removeTop: true,
              removeBottom: true,
              removeLeft: true,
              removeRight: true,
              context: context,
              child: subCategories
          )
          ]
        );
      } else {
        return Container(
            height: 30.0,
            child: ListTile(
                title: Row(
                    children: [
                      Text("    " + _categories[index]['name']),
                      Transform.scale(
                          scale: 0.75,
                          child:
                          Icon(Icons.arrow_back)
                      )
                    ]
                ),
                onTap: () {
                  flickCategories();
                  idHandler.toggleId(_categories[index]['id']);
                }
            ),
            padding: EdgeInsets.all(0.0),
            margin: EdgeInsets.all(0.0)
        );
      }
    } else {
      return Container(
          height: 30.0,
          child: ListTile(
              title: Text("    "+_categories[index]['name']),
              onTap: () {
                  //TODO: implement category navigation
              }
          ),
          padding: EdgeInsets.all(0.0),
          margin: EdgeInsets.all(0.0)
      );
    }
  }

  void buildSubCategories(var parent, int depth){
    if(parent['subCategories']!=null){
      List subCategoryList = parent['subCategories'];
      int subCategoryCount = subCategoryList.length;
      //Column subCats = new Column();
          if(idHandler.isIdOpen(parent['id'])) {
            for (var i = 0; i < subCategoryCount; i++) {
              String formattedCategory = indentCategory(parent['subCategories'][i]['name'], depth);
              _tempSubCats.add(
                parent['subCategories'][i]['subCategories']!=null ?
                    Container(
                        height:20.0,
                        child:
                          ListTile(
                            title: idHandler.isIdOpen(parent['subCategories'][i]['id']) ?
                              Row(
                                children:[
                                    Text(formattedCategory),
                                    Transform.rotate(
                                      angle:-math.pi/2,
                                      child:
                                        Transform.scale(
                                          scale:0.75,
                                          child:
                                            Icon(Icons.arrow_back)
                                        )
                                    )
                                ]
                              )
                              :
                              Row(
                                children: [
                                    Text(formattedCategory),
                                    Transform.scale(
                                      scale: 0.75,
                                      child:
                                        Icon(Icons.arrow_back)
                                    )
                                ]
                              ),
                            onTap: (){
                              flickCategories();
                              idHandler.toggleId(parent['subCategories'][i]['id']);
                            }
                          )
                    )
                    :
                    Container(
                      height:20.0,
                      child:
                        ListTile(
                          title: Text(formattedCategory),
                          onTap: (){
                            //TODO: implement category navigation
                          }
                        )
                    )
              );
              buildSubCategories(parent['subCategories'][i], depth+1);
            }
          }
      }
  }

  String indentCategory(String input, int amount){
    String output='';
      for(var i=0; i<amount; i++){
          output += '    ';
      }
      output+=input;
      return output;
  }

  @override
  _navDrawerState createState() => _navDrawerState();
}
class _navDrawerState extends State<navDrawer>{
  @override
  Widget build(BuildContext Context){
      return Drawer(
          child:
          Container(
            padding:EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 5),
            child:
              Column(
                children:[
                      Container(
                          height:80.0,
                          width:double.infinity,
                          child:
                          DrawerHeader(
                              child: Text('Menu'),
                              decoration: BoxDecoration(
                                  gradient: LinearGradient(
                                      begin: Alignment.topLeft,
                                      end: Alignment.bottomRight,
                                      colors: <Color>[
                                        Colors.grey,
                                        Colors.red
                                      ])
                              )
                          ),
                          margin:EdgeInsets.all(0.0),
                          padding:EdgeInsets.all(0.0)
                      ),
                      Expanded(
                        child:
                          ListView(
                              padding:EdgeInsets.zero,
                              children: <Widget>[
                                widget._expandCategories?
                                Column(
                                  children:[
                                    Container(
                                      height:40.0,
                                      child: ListTile(
                                        title: Row(
                                          children: [
                                            Text('Categories'),
                                            Transform.rotate(
                                              angle: -math.pi/2,
                                              child:
                                                Transform.scale(
                                                  scale: 0.75,
                                                  child:
                                                    Icon(Icons.arrow_back)
                                              )
                                            )
                                          ]
                                        ),
                                        onTap:() {
                                          _expandCat();
                                        }
                                      )
                                    ),
                                    MediaQuery.removePadding(
                                      removeTop:true,
                                      context: context,
                                      child:
                                        SizedBox(
                                          height:300.0,
                                          child: widget._categoryList,
                                          
                                        )
                                    )
                                  ]
                                )
                                :Container(
                                    height:40.0,
                                    child:
                                    ListTile(
                                        title: Row(
                                            children: [
                                              Text('Categories'),
                                              Transform.scale(
                                                  scale: 0.75,
                                                  child:
                                                  Icon(Icons.arrow_back)
                                              )
                                            ]
                                        ),
                                        onTap:(){
                                          _expandCat();
                                          //Update state of the app
                                        }
                                    ),
                                    margin:EdgeInsets.all(0.0),
                                    padding:EdgeInsets.all(0.0)
                                ),
                                Container(
                                    height:40.0,
                                    child:
                                    ListTile(
                                        title:Text('Your quotes'),
                                        onTap:(){
                                          //Update state of the app
                                        }
                                    ),
                                    margin:EdgeInsets.all(0.0),
                                    padding:EdgeInsets.all(0.0)
                                ),
                                widget._expandAccount?
                                Column(
                                  children:[
                                    Container(
                                        height:40.0,
                                        child:
                                        ListTile(
                                            title: Row(
                                                children:[
                                                  Text('Your account'),
                                                  Transform.rotate(
                                                    angle:-math.pi/2,
                                                    child:
                                                    Transform.scale(
                                                        scale:0.75,
                                                        child:
                                                        Icon(Icons.arrow_back)
                                                    )
                                                  )
                                                ]
                                            ),
                                            onTap:(){
                                              _expandAcc();
                                            }
                                        ),
                                        margin:EdgeInsets.all(0.0),
                                        padding:EdgeInsets.all(0.0)
                                    ),
                                    Container(
                                      height:30.0,
                                      child:
                                        ListTile(
                                          title:Text('        Your Information'),
                                          onTap:(){

                                          }
                                        )
                                    ),
                                    Container(
                                        height:30.0,
                                        child:
                                        ListTile(
                                            title:Text('        Your Address'),
                                            onTap:(){

                                            }
                                        )
                                    ),
                                    Container(
                                        height:30.0,
                                        child:
                                        ListTile(
                                            title:Text('        Listed Equipment'),
                                            onTap:(){

                                            }
                                        )
                                    ),
                                    Container(
                                        height:30.0,
                                        child:
                                        ListTile(
                                            title:Text('        Add Equipment'),
                                            onTap:(){

                                            }
                                        )
                                    ),
                                  ]
                                )
                                :Container(
                                    height:40.0,
                                    child:
                                    ListTile(
                                        title: Row(
                                            children:[
                                              Text('Your account'),
                                              Transform.scale(
                                                  scale:0.75,
                                                  child:
                                                  Icon(Icons.arrow_back)
                                              )
                                            ]
                                        ),
                                        onTap:(){
                                          _expandAcc();
                                        }
                                    ),
                                    margin:EdgeInsets.all(0.0),
                                    padding:EdgeInsets.all(0.0)
                                )
                              ]
                          ),
                      )
                  ]
              )
          )
      );
  }
  void _expandCat(){
      setState((){
        widget._expandCategories=!widget._expandCategories;
      });
  }
  void _expandAcc(){
      setState((){
        widget._expandAccount=!widget._expandAccount;
      });
  }
}

NOTE: idHandler is a public member of main.dart.注意:idHandler 是 main.dart 的公共成员。
NOTE2: flickCategories() is one of my attempts at updating the state.注意 2:flickCategories() 是我更新 state 的尝试之一。

In the screenshot below you can see what I mean:在下面的屏幕截图中,您可以看到我的意思:
打开类别

If I click Electrical Equipment then I have to click Categories twice to make it redraw and I have to scroll back to where it was in the list.如果单击“电气设备”,则必须单击“类别”两次以使其重绘,并且必须滚动回它在列表中的位置。

So, how do I make the state update when one of my categories gets clicked?那么,当我的一个类别被点击时,如何进行 state 更新?
Do I need something like a stateful category widget?我需要像有状态类别小部件这样的东西吗?
I'm trying to make it look responsive with arrows and indents and etc.我正在尝试通过箭头和缩进等使其看起来具有响应性。

I figured this out on my own.这是我自己想出来的。

I needed to make a productCategory stateful widget and update its state from within the widget.我需要制作一个 productCategory 有状态小部件并从小部件中更新其 state。

Each productCategory widget has a List representing the subCategories.每个 productCategory 小部件都有一个表示子类别的列表。 During my recursion I add to the subCategories for each productCategory.在我的递归过程中,我为每个产品类别添加了子类别。

The productCategory widget redraws itself properly because I call setState() which has the added bonus of keeping the scroll position where it is. productCategory 小部件会正确重绘自身,因为我调用了 setState(),它具有将滚动 position 保持在原位的额外好处。

Here's my productCategory widget:这是我的 productCategory 小部件:

class productCategory extends StatefulWidget{
  String _id = '';
  String _name = '';
  List<productCategory> _subCategories = [];

  productCategory(String id, String name){
    _id = id;
    _name = name;
  }

  void addAllSubCategories(List<productCategory> subCats){
    _subCategories.addAll(subCats);
  }

  void addSubCategory(productCategory cat){
    _subCategories.add(cat);
  }

  void setName(String name){
    _name = name;
  }

  void setId(String id){
    _id = id;
  }

  @override
  _productCategoryState createState() => _productCategoryState();

}
class _productCategoryState extends State<productCategory>{
  @override
  Widget build(BuildContext context) {
    if(widget._subCategories.isNotEmpty){
      if(idHandler.isIdOpen(widget._id)){
        return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Container(
                  height: 30.0,
                  child: ListTile(
                      title: Row(
                          children: [
                            Text(widget._name),
                            Transform.rotate(
                                angle: -math.pi/2,
                                child:
                                Transform.scale(
                                    scale: 0.75,
                                    child:
                                    Icon(Icons.arrow_back)
                                )
                            )
                          ]
                      ),
                      onTap: () {
                        setState((){
                          idHandler.toggleId(widget._id);
                        });
                      }
                  ),
                  padding: EdgeInsets.all(0.0),
                  margin: EdgeInsets.all(0.0)
              ),
              MediaQuery.removePadding(
                  removeTop: true,
                  removeBottom: true,
                  removeLeft: true,
                  removeRight: true,
                  context: context,
                  child: ListView.builder(
                    physics: NeverScrollableScrollPhysics(),
                    shrinkWrap: true,
                    padding: EdgeInsets.all(0.0),
                    itemCount: widget._subCategories.length,
                    itemBuilder: (BuildContext context, int index){
                      return widget._subCategories[index];
                    }
                  )
              )
            ]
        );
      } else {
        return Container(
            height: 30.0,
            child: ListTile(
                title: Row(
                    children: [
                      Text(widget._name),
                      Transform.scale(
                          scale: 0.75,
                          child:
                          Icon(Icons.arrow_back)
                      )
                    ]
                ),
                onTap: () {
                  setState((){
                    idHandler.toggleId(widget._id);
                  });
                }
            ),
            padding: EdgeInsets.all(0.0),
            margin: EdgeInsets.all(0.0)
        );
      }
    } else {
      return Container(
          height: 30.0,
          child: ListTile(
              title: Text(widget._name),
              onTap: () {
                //TODO: implement category navigation
              }
          ),
          padding: EdgeInsets.all(0.0),
          margin: EdgeInsets.all(0.0)
      );
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM