Flutter CustomScrollView和范围模型问题

时间:2019-04-09 13:53:06

标签: dart flutter flutter-dependencies

最近一段时间我正面临一些问题。我正在为我的应用程序使用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(这是我的searchwidget):

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,
              ),
            ),
          ],
        ),
      ),
    );   } }

relation_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不在init状态。但是我不确定,我希望有人知道这段代码有什么问题。

预先感谢

问候,

Jente

更新: 当我在initializeValues()的relation_scoped_model中将atTheEnd设置为false并将hasMorePages设置为true时,我的customcrollview工作正常。

我仍然面临的唯一问题是我得到了重复,这是因为我的屏幕重建了两次。

1 个答案:

答案 0 :(得分:0)

使用作用域模型时,应使用作用域模型后代包装应该对更改一一响应的小部件。这样,只有那些小部件可以重建,而不是整个页面重建。