Проблемы модели Flutter CustomScrollView и Scoped

В последнее время я сталкиваюсь с некоторыми проблемами. Я использую для своего приложения пакет scoped_model. Но у меня проблема в том, что когда я ищу что-то или переворачиваю экран в альбомный режим, он строится дважды.

Когда это происходит, я получаю дубликат json, потому что он дважды перестраивается.

У меня возникает еще одна проблема, когда я искал и возвращаюсь, мое настраиваемое представление прокрутки больше не работает. Я больше не могу прокручивать вниз.

Я написал следующие файлы:

main.dart:

class GetRelations extends StatefulWidget {
  @override
  RelationsPage createState() => RelationsPage();
}

class RelationsPage extends State<GetRelations> {
  final RelationScopedModel relationScopedModel = RelationScopedModel();

  @override
  Widget build(BuildContext context) {
    relationScopedModel.initializeValues();

    return Scaffold(
      body: ScopedModel<RelationScopedModel>(
        model: relationScopedModel,
        child: Container(
          child: SearchScreen(),
        ),
      ),
    );
  }
}

search_screen.dart:

class SearchScreen extends StatefulWidget {
  @override
  SearchScreenState createState() {
    return new SearchScreenState();
  }
}

class SearchScreenState extends State<SearchScreen> {
  final RelationScopedModel relationScopedModel = RelationScopedModel();
  ScrollController controller;
  int page = 0;
  bool atEnd = false;

  @override
  void initState() {
    super.initState();
    controller = new ScrollController()..addListener(_scrollListener);
  }

  @override
  void dispose() {
    super.dispose();
    controller.dispose();
  }

  void _scrollListener() {
    var props = RelationScopedModel.of(context);

    /// When reload we check if skip == 0, if skip is 0 then page has to become 0 too.
    if (props.skip == 0) {
      page = 0;
    }

    /// Checking if user is at the end of the screen, then let's receive some new content.
    if (controller.position.pixels == controller.position.maxScrollExtent) {
      /// If it is at the end, we have to set atEnd to true.
      if (props.atTheEnd == true) {
        atEnd = props.atTheEnd;
        return;
      }

      /// If it has no more pages, return.
      if (props.hasMorePages == false) {
        return;
      }

      /// If it is has more stuff, load it in!
      if (!props.isLoadingMore && props.hasMorePages) {
        page++;
        props.getRelations(props.search, page);
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    /// Go back to last page by using WillPopScope.
    return WillPopScope(
      onWillPop: () {
        Navigator.pop(context);
      },
      child: new Scaffold(
        drawer: new DrawerOnly(),

        /// We are using Scoped Model to load the json in the sliver list.
        body: ScopedModelDescendant<RelationScopedModel>(
          builder: (context, child, model) {
            return CustomScrollView(
              controller: controller,
              slivers: <Widget>[
                SliverAppBar(
                  title: SearchWidget(
                    performSearch: model.getRelations,
                  ),
                  floating: true,
                  pinned: true,
                ),
                model.isLoading && model.atTheEnd
                    ? SliverFillRemaining(
                        child: Center(
                          child: CircularProgressIndicator(),
                        ),
                      )
                    : model.getRelationCount() < 1
                        ? SliverFillRemaining(
                            child: Center(
                              child: Text(
                                model.statusText,
                                style: Theme.of(context).textTheme.headline,
                              ),
                            ),
                          )
                        : SliverList(
                            delegate: SliverChildBuilderDelegate(
                              (context, index) {
                                if (index == model.getRelationCount() + 1) {
                                  if (model.hasMorePages == true) {
                                    return Padding(
                                      padding: const EdgeInsets.symmetric(
                                          vertical: 16.0),
                                      child: Center(
                                          child: CircularProgressIndicator()),
                                    );
                                  }
                                  return Container(width: 0, height: 0);
                                } else if (index == 0) {
                                  return Container(
                                    padding: const EdgeInsets.all(16),
                                    decoration: BoxDecoration(
                                        border: Border(
                                            bottom: BorderSide(
                                                color: Colors.grey[300]))),
                                    child: Text(
                                      "Relaties",
                                      style: Theme.of(context)
                                          .textTheme
                                          .body2
                                          .copyWith(color: Colors.white),
                                    ),
                                  );
                                } else {
                                  return Container(
                                    child: Column(
                                      children: <Widget>[
                                        InkWell(
                                          child: RelationItem(
                                              model.relation[index - 1]),
                                          onTap: () {
                                            var id =
                                                model.relation[index - 1].id;
                                            Navigator.of(context).push(
                                                new MaterialPageRoute(
                                                    builder: (BuildContext
                                                            context) =>
                                                        new DetailScreen(id)));
                                          },
                                        ),
                                      ],
                                    ),
                                  );
                                }
                              },
                              childCount: model.getRelationCount() + 2,
                            ),
                          )
              ],
            );
          },
        ),
      ),
    );
  }
}

search.dart (это мой виджет поиска):

class SearchWidget extends StatelessWidget {   final performSearch;

  const SearchWidget({Key key, @required this.performSearch}) : super(key: key);

  @override   Widget build(BuildContext context) {
    print('wat gebeurt er');
    return Card(
      elevation: 3.0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(Radius.circular(2.0)),
      ),
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 15.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            GestureDetector(
              child: Icon(
                Icons.search,
                color: Colors.white,
              ),
              onTap: () {},
            ),
            SizedBox(
              width: 10.0,
            ),
            Expanded(
              child: TextField(
                decoration: InputDecoration(
                    hintStyle: TextStyle(fontSize: 14.0, color: Colors.white),
                    border: InputBorder.none,
                    hintText: "Zoeken..."),
                onChanged: (String search) {
                  if (search.length > 2) {
                    performSearch(search);
                  }
                  if (search.length == 0) {
                    performSearch(search);
                  }
                },
              ),
            ),
            InkWell(
              onTap: () {
                Navigator.of(context).push(
                    MaterialPageRoute(builder: (context) => SettingsScreen()));
              },
              child: Icon(
                FontAwesomeIcons.slidersH,
                color: Colors.white,
              ),
            ),
          ],
        ),
      ),
    );   } }

Relationship_scoped_model.dart:

class RelationScopedModel extends Model {
  List<GetRelation> _relation = [];
  List<GetContacts> _contacts = [];
  List<GetLocations> _locations = [];
  bool _isLoading = false;
  String _statusText = "Start met zoeken...";
  bool _hasMorePages = true;
  int _skip = 0;
  int skipMore = 0;
  String _search;
  bool _isLoadingMore = false;
  bool atTheEnd = false;

  List<Map<String, String>> _listingTypeList = [
    {"name": "Buy", "value": "buy"},
    {"name": "Rent", "value": "rent"},
  ];
  String _listingType;

  List<Map<String, String>> _sortList = [
    {"name": "Relevancy", "value": "relevancy"},
    {"name": "Bedroom (Ascending)", "value": "bedroom_lowhigh"},
    {"name": "Bedroom (Descending)", "value": "bedroom_highlow"},
    {"name": "Price (Ascending)", "value": "price_lowhigh"},
    {"name": "Price (Descending)", "value": "price_highlow"},
    {"name": "Newest", "value": "newest"},
    {"name": "Oldest", "value": "oldest"},
    {"name": "Random", "value": "random"},
    {"name": "Distance", "value": "distance"}
  ];

  String _sort;

  List<GetRelation> get relation => _relation;

  List<GetContacts> get contacts => _contacts;

  List<GetLocations> get locations => _locations;

  bool get isLoading => _isLoading;

  String get statusText => _statusText;

  bool get hasMorePages => _hasMorePages;

  String get search => _search;

  int get skip => _skip;

  bool get isLoadingMore => _isLoadingMore;

  List<Map<String, String>> get listingTypeList => _listingTypeList;

  String get listingType => _listingType;

  List<Map<String, String>> get sortList => _sortList;

  String get sort => _sort;

  int getRelationCount() => _relation.length;

  void initializeValues() async {
    _relation.clear();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    _listingType = prefs.getString('listingType') ?? 'rent';
    _sort = prefs.getString('sort') ?? 'relevancy';
    getRelations(search);
  }

  var _skiptotal = 10;
  var lastSearch;

  Future<dynamic> _getData(String search, [int page = 0]) async {

    /// When page is 0 we don't have to skip content, if page is 1 or higher it will have to skip content
    if (page != 0) {
      var skipPage = page * _skiptotal;
      _skip = skipPage;
    }

    /// If page == 0, we have to set page and _skip to 0.
    else {
      _skip = 0;
      page = 0;
    }

    /// When nothing is filled in the input search, we have to set search to single quotes because otherwise it will search on null.
    if (search == null) {
      search = '';
    }

    if (lastSearch != search) {
      _relation.clear();
      lastSearch = '';
      lastSearch = search;
      page + 1;
    }

    String _credentials;
    SharedPreferences pref = await SharedPreferences.getInstance();
    _credentials = (pref.getString("credentials") ?? "Empty");

    var res = await http.get(
        Uri.encodeFull("$cf_api_RelationsUrl" +
            "?$cf_api_SkipParameter=$_skip&$cf_api_SearchParameter=$search&$cf_api_LimitParameter=10"),
        headers: {
          "content-type": "application/json",
          "accept": "application/json",
          'cookie': '$_credentials'
        });

    var decodedJson = json.decode(res.body, reviver: (k, v) {
      if (k == "status" && v == false) {
        _hasMorePages = false;
        return v;
      } else {
        return v;
      }
    });

    if (_hasMorePages == false) {
      return decodedJson;
    } else {
      List list = List();
      list = json.decode(res.body) as List;
      if (list.length < 10) {
        atTheEnd = true;
        _hasMorePages = false;
        return decodedJson;
      }
    }

    return decodedJson;
  }

  Future getRelations(String search, [int page = 0]) async {
    if (page == 0) {
      page = 0;
      _isLoading = true;
      _relation.clear();
    } else {
      _isLoadingMore = true;
    }

    _search = search;
    var responseData = await _getData(search, page);
    notifyListeners();

    var result = responseData
        .map(
            (data) => serializers.deserializeWith(GetRelation.serializer, data))
        .toList();

    result.forEach((relations) {
      _relation.add(relations);
    });

    if (result.isEmpty) {
      _statusText = "Nothing Found";
    }

    if (page == 0) {
      _isLoading = false;
    } else {
      _isLoadingMore = false;
    }

    if (atTheEnd == true) {
      return true;
    }

//    notifyListeners();
  }

  void setListingType(String value) async {
    _listingType = value;
    getRelations(search);
    notifyListeners();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('listingType', _listingType);
  }

  void setSort(String value) async {
    _sort = value;
    getRelations(search);
    notifyListeners();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('sort', _sort);
  }

  /// Wraps [ScopedModel.of] for this [Model].
  static RelationScopedModel of(BuildContext context) =>
      ScopedModel.of<RelationScopedModel>(context);
}

Я предполагаю, что проблема в том, что мой потомок scopedmodeldescendant не находится в состоянии инициализации. Но я не уверен и надеюсь, что кто-нибудь знает, в чем проблема с этим кодом.

заранее спасибо

Привет,

Jente

ОБНОВЛЕНИЕ: я получил, что мой customscrollview работает, когда я установил atTheEnd на false и hasMorePages на true в моем Relations_scoped_model в initializeValues ​​().

Единственная проблема, с которой я все еще сталкиваюсь, - это то, что я получаю дубликаты, потому что мой экран дважды перестраивается.


person SJente    schedule 09.04.2019    source источник


Ответы (1)


При использовании модели с ограниченной областью виджета следует обернуть виджеты, которые должны реагировать на изменения один за другим, потомками модели с ограниченной областью видимости. Таким образом, можно будет перестроить только эти виджеты, а не всю страницу.

person Aknahseh_tg    schedule 25.02.2020