首页 > 解决方案 > 在 ListView.builder() 中选中/取消选中 ListTiles - Flutter

问题描述

我有一个需要ListView.builder();showModalBottomSheet(); 点击时选择/取消选择多个项目一切都很好,但需要关闭模式并再次显示以应用更改,另一件事是ListTiles有时重复多次,功能emptyList无法正常工作。

    import 'package:http/http.dart' as http;
    import 'dart:convert';
    import 'dart:async';
    import 'package:flutter/material.dart';
    import 'book_details.dart' show BookDetails;

    class Explore extends StatefulWidget {
      @override
      _ExploreState createState() => _ExploreState();
    }

    var _books,
        _categories,
        _arranges,
        _currentCategory,
        _selected,
        _primeColor,
        _currentFilter,
        _isThereIsFilters,
        _booksContainer,
        _booksWithFilters,
        _isLoading,
        _noBooks,
        _itemIcon;

    final GlobalKey<ScaffoldState> _scaffoldKeyExplore =
        new GlobalKey<ScaffoldState>();

    List<String> _getCats = new List();
    List<String> _getArrs = new List();

    void _insertCategories() {
      for (int i = 0; i < _categories.length; i++) {
        _getCats.add(_categories[i]);
      }
      _getCats.sort();
    }

    void _insertArranges() {
      for (int i = 0; i < _arranges.length; i++) {
        _getArrs.add(_arranges[i]);
      }
    }

    class _ExploreState extends State<Explore> with TickerProviderStateMixin {

      onCatChange(String category) {
        setState(() {
          _currentCategory = category;
        });
      }

      @override
      void initState() {
        super.initState();
        _primeColor = Color.fromRGBO(239, 89, 39, 1.0);
        _categories = ["أول", "ثاني", "ثالث", "رابع", "خامس"];
        _arranges = ["أول", "ثاني", "ثالث", "رابع", "خامس"];
        _currentFilter = _arranges[0];
        _selected = [];
        _isThereIsFilters = false;
      }

      void emptyList(List list) {
        for (var i = 0; i < list.length; i++) {
          list.remove(list[i]);
        }
      }

      _showSheet(String type) {
        switch (type) {
          case "filters":
            showModalBottomSheet(
                context: _scaffoldKeyExplore.currentContext,
                builder: (BuildContext context) {
                  return Directionality(
                    textDirection: TextDirection.rtl,
                    child: Container(
                        child: Column(children: <Widget>[
                      Expanded(
                        child: new ListView.builder(
                            itemCount: _getArrs[0] != null ? _getArrs.length : 0,
                            itemBuilder: (BuildContext context, int i) {
                              return new RadioListTile(
                                  title: Text(_getArrs[i]),
                                  value: _getArrs[i],
                                  groupValue: _currentFilter,
                                  onChanged: (val) {
                                    setState(() {
                                      _currentFilter = val;
                                    });
                                  });
                            }),
                      )
                    ])),
                  );
                });
            break;

          case "categories":
          default:
            showModalBottomSheet(
                context: _scaffoldKeyExplore.currentContext,
                builder: (BuildContext context) {
                  return Directionality(
                    textDirection: TextDirection.rtl,
                    child: Container(
                        child: Column(children: <Widget>[
                          Container(
                            color: _primeColor,
                            child: Row(
                                mainAxisSize: MainAxisSize.max,
                                mainAxisAlignment: MainAxisAlignment.end,
                                children: <Widget>[
                                  IconButton(
                                      icon: Icon(Icons.close, color: Colors.white),
                                      onPressed: () {
                                        emptyList(_selected);
                                        //Navigator.pop(context);
                                        //_showSheet(type);
                                      }),
                                  IconButton(
                                      icon:
                                          Icon(Icons.done_all, color: Colors.white),
                                      onPressed: () {
                                        if (_selected.length > 0) {
                                          _getFilteredBooks(_selected);
                                          setState(() {
                                            _isThereIsFilters = true;
                                          });
                                        } else {
                                          setState(() {
                                            _isThereIsFilters = false;
                                          });
                                        }
                                        Navigator.pop(context);
                                      })
                                ]),
                          ),
                          Expanded(
                            child: new ListView.builder(
                                itemCount: _getCats != null ? _getCats.length : 0,
                                itemBuilder: (BuildContext context, int i) {
                                  final _isSelected = _selected.contains(_getCats[i]);
                                  return new ListTile(
                                      leading: Icon(Icons.category),
                                      trailing: _isSelected ? Icon(Icons.done) : null,
                                      title: Text(_getCats[i]),
                                      onTap: () {
                                        setState(() {
                                          _isSelected
                                              ? _selected.remove(_getCats[i])
                                              : _selected.add(_getCats[i]);
                                        });
                                        //Navigator.pop(context);
                                        //_showSheet(type);
                                      });
                                }),
                          )
                        ])),
                  );
                });
            break;
        }
      }

      @override
      Widget build(BuildContext context) {
        return new Directionality(
            textDirection: TextDirection.rtl,
            child: new Scaffold(
              key: _scaffoldKeyExplore,
              appBar:
                  AppBar(title: Text("استكشاف"), elevation: 0.0, actions: <Widget>[
                IconButton(
                    icon: Icon(Icons.category, color: _primeColor),
                    onPressed: () => _showSheet("categories")),
                IconButton(
                    icon: Icon(Icons.filter_list, color: _primeColor),
                    onPressed: () => _showSheet("filters"))
              ]),
              body: Center(child: Text("Nothing..."));
            ));
      }
    }

谢谢

标签: flutter

解决方案


需要关闭模态并再次显示以应用更改

发生这种情况是因为需要再次调用showModalBottomSheet'以反映更改。builder在 Flutter 中,StatefulWidgets 应该能够在状态发生变化时随时重建——这里不是这种情况,因为显示了底部工作表。

为什么我会遇到这个问题(在元级别上)?

Storing the state in StatefulWidgets is useful for saving UI state, but you quickly outgrow this technique if you want to store some "app state" or "data state" that is independent of the screen it's on.

It is finally time to fundamentally rethink your state management and settle on a full-fledged state management pattern that decouples the state from the widgets. Luckily, there are a few to choose from:

  • Making everything global, like you did above. This is generally not a good idea, as you break the contract of setState (state can be modified without the widgets being notified). Also, you break hot restart and stuff like that.
  • Using an InheritedWidget, where widgets below a root widget can access the same state.
  • Using a ScopedModel, which builds on top of that.
  • Using the infamous BLoC pattern, which also builds on top of the InheritedWidget, but adds some Stream-y stuff to make everything more reactive.
  • Probably many more.

Here is a great Youtube video about state management from Google I/O, where several patterns are being presented.

Anyways, are bottom sheets the right widget for the task ahead?

According to the Material Design spec, the modal bottom sheet is "an alternative to inline menus or simple dialogs on mobile, providing room for additional items, longer descriptions, and iconography".

More concrete, the showModalBottomSheet function is designed to show a widget that doesn't affect the parent over time, but rather - if at all - at a single point in time. That's why it returns a Future<T>, not a Stream<T>.

Be aware that you are trying to use the bottom sheet in a way that it's not intended to be used. In your case, I'd recommend just using a new screen.


推荐阅读