简体   繁体   English

Flutter CustomScrollView 和 Scoped 模型问题

[英]Flutter CustomScrollView and Scoped model problems

I'm facing some issues quite the time lately.我最近经常面临一些问题。 I'm using the scoped_model package for my application.我正在为我的应用程序使用scoped_model包。 But i'm getting the problem that when i'm searching for stuff or i turn my screen to landscape modus it builds twice.但是我遇到的问题是,当我在搜索内容或将屏幕切换为横向模式时,它会构建两次。

When this happens i'm getting duplicate json because it is rebuilding twice.发生这种情况时,我会收到重复的 json,因为它正在重建两次。

I'm getting another issue when i have searched and i go back my custom scroll view is not working anymore.当我搜索并返回时,我遇到了另一个问题,我的自定义滚动视图不再工作。 I can't scroll down anymore.我不能再向下滚动了。

I have writtin the following files:我已经写了以下文件:

main.dart:主要.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: 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 (this is my searchwidget): 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,
              ),
            ),
          ],
        ),
      ),
    );   } }

relation_scoped_model.dart:关系范围模型.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);
}

I'm guessing that the problem is that my scopedmodeldescendant isn't in the init state.我猜问题是我的 scopedmodeldescendant 不在 init 状态。 But i'm not sure and i hope somebody knows what the problem is with this code.但我不确定,我希望有人知道这段代码有什么问题。

Thanks in advance提前致谢

Greetings,你好,

Jente珍特

UPDATE: I got my customscrollview is working when i set atTheEnd to false and hasMorePages to true in my relation_scoped_model at initializeValues().更新:当我在 initializeValues() 的relation_scoped_model 中将 atTheEnd 设置为 false 并将 hasMorePages 设置为 true 时,我的 customcrollview 正在工作。

The only problem i'm still facing is that i'm getting duplicates, which happens because my screen is rebuilding twice.我仍然面临的唯一问题是我得到了重复,这是因为我的屏幕正在重建两次。

When using scoped model, you should wrap the widgets that should respond to changes one by one with scoped model descendants.使用范围模型时,您应该将应响应更改的小部件与范围模型后代一一包装。 This way only those widgets can rebuild rather than the whole page rebuilding.这样只有那些小部件可以重建而不是整个页面重建。

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

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