栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Flutter中的分页/无限滚动,具有缓存和实时失效功能

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Flutter中的分页/无限滚动,具有缓存和实时失效功能

这是最新版本,感谢一些非常有用的建议

import 'dart:math';import 'package:flutter/material.dart';import 'package:flutter/scheduler.dart';import 'package:quiver/cache.dart';import 'package:quiver/collection.dart';typedef Future<List<T>> PageFuture<T>(int pageIndex);typedef Widget ItemBuilder<T>(BuildContext context, int index, T entry);typedef Widget ErrorBuilder(BuildContext context, dynamic error);class LazyListView<T> extends StatefulWidget {  final int pageSize;  final PageFuture<T> pageFuture;  final Stream<int> countStream;  final ItemBuilder<T> itemBuilder;  final IndexedWidgetBuilder placeholderBuilder;  final WidgetBuilder waitBuilder;  final WidgetBuilder emptyResultBuilder;  final ErrorBuilder errorBuilder;  final double velocityThreshold;  LazyListView({    @required this.pageSize,    @required this.pageFuture,    @required this.countStream,    @required this.itemBuilder,    @required this.placeholderBuilder,    this.waitBuilder,    this.emptyResultBuilder,    this.errorBuilder,    this.velocityThreshold = 128,  })  : assert(pageSize > 0),        assert(pageFuture != null),        assert(countStream != null),        assert(itemBuilder != null),        assert(placeholderBuilder != null),        assert(velocityThreshold >= 0);  @override  _LazyListViewState<T> createState() => _LazyListViewState<T>();}class _LazyListViewState<T> extends State<LazyListView<T>> {  Map<int, PageResult<T>> map;  MapCache<int, PageResult<T>> cache;  dynamic error;  int totalCount = -1;  bool _frameCallbackInProgress = false;  @override  void initState() {    super.initState();    _initCache();    widget.countStream.listen((int count) {      totalCount = count;      _initCache();      setState(() {});    });  }  @override  Widget build(BuildContext context) {    //debugPrintBeginframeBanner = true;    //debugPrintEndframeBanner = true;    //print('build');    if (error != null && widget.errorBuilder != null) return widget.errorBuilder(context, error);    if (totalCount == -1 && widget.waitBuilder != null) return widget.waitBuilder(context);    if (totalCount == 0 && widget.emptyResultBuilder != null) return widget.emptyResultBuilder(context);    return ListView.builder(      physics: _LazyListViewPhysics(velocityThreshold: widget.velocityThreshold),      itemCount: max(totalCount, 0),      itemBuilder: (context, index) {        // print('builder $index');        var page = index ~/ widget.pageSize;        final pageResult = map[page];        final value = pageResult?.items?.elementAt(index % widget.pageSize);        if (value != null) {          return widget.itemBuilder(context, index, value);        }        // print('$index ${Scrollable.of(context).position.activity.velocity}');        if (!Scrollable.recommendDeferredLoadingForContext(context)) {          cache.get(page, ifAbsent: _loadPage).then(_reload).catchError(_error);        } else if (!_frameCallbackInProgress) {          _frameCallbackInProgress = true;          SchedulerBinding.instance.scheduleframeCallback((d) => _deferredReload(context));        }        return widget.placeholderBuilder(context, index);      },    );  }  Future<PageResult<T>> _loadPage(int index) async {    print('load $index');    var list = await widget.pageFuture(index);    return PageResult(index, list);  }  void _initCache() {    map = LruMap<int, PageResult<T>>(maximumSize: 50 ~/ widget.pageSize);    cache = MapCache<int, PageResult<T>>(map: map);  }  void _error(dynamic e, StackTrace stackTrace) {    if (widget.errorBuilder == null) {      throw e;    }    setState(() => error = e);  }  void _reload(PageResult<T> value) => _doReload(value.index);  void _deferredReload(BuildContext context) {    print('_deferredReload');    if (!Scrollable.recommendDeferredLoadingForContext(context)) {      _frameCallbackInProgress = false;      _doReload(-1);    } else {      SchedulerBinding.instance.scheduleframeCallback((d) => _deferredReload(context), rescheduling: true);    }  }  void _doReload(int index) {    // print('reload $index');    setState(() {});  }}class PageResult<T> {  /// Page index of this data.  final int index;  final List<T> items;  PageResult(this.index, this.items);}class _LazyListViewPhysics extends AlwaysScrollableScrollPhysics {  final double velocityThreshold;  _LazyListViewPhysics({    @required this.velocityThreshold,    ScrollPhysics parent,  }) : super(parent: parent);  @override  recommendDeferredLoading(double velocity, ScrollMetrics metrics, BuildContext context) {    // print('velocityThreshold: $velocityThreshold');    return velocity.abs() > velocityThreshold;  }  @override  _LazyListViewPhysics applyTo(ScrollPhysics ancestor) {    // print('applyTo($ancestor)');    return _LazyListViewPhysics(velocityThreshold: velocityThreshold, parent: buildParent(ancestor));  }}

更新#1

这是一个新版本,可确保

setState
在卸载小部件时不会调用期货。

import 'dart:async';import 'dart:math';import 'package:flutter/material.dart';import 'package:flutter/scheduler.dart';import 'package:quiver/cache.dart';import 'package:quiver/collection.dart';typedef Future<List<T>> PageFuture<T>(int pageIndex);typedef Widget ItemBuilder<T>(BuildContext context, int index, T entry);typedef Widget ErrorBuilder(BuildContext context, dynamic error);class LazyListView<T> extends StatefulWidget {  final int pageSize;  final PageFuture<T> pageFuture;  final Stream<int> countStream;  final ItemBuilder<T> itemBuilder;  final IndexedWidgetBuilder placeholderBuilder;  final WidgetBuilder waitBuilder;  final WidgetBuilder emptyResultBuilder;  final ErrorBuilder errorBuilder;  final double velocityThreshold;  LazyListView({    @required this.pageSize,    @required this.pageFuture,    @required this.countStream,    @required this.itemBuilder,    @required this.placeholderBuilder,    this.waitBuilder,    this.emptyResultBuilder,    this.errorBuilder,    this.velocityThreshold = 128,  })  : assert(pageSize > 0),        assert(pageFuture != null),        assert(countStream != null),        assert(itemBuilder != null),        assert(placeholderBuilder != null),        assert(velocityThreshold >= 0);  @override  _LazyListViewState<T> createState() => _LazyListViewState<T>();}class _LazyListViewState<T> extends State<LazyListView<T>> {  Map<int, PageResult<T>> map;  MapCache<int, PageResult<T>> cache;  dynamic error;  int totalCount = -1;  bool _frameCallbackInProgress = false;  StreamSubscription<int> countStreamSubscription;  @override  void initState() {    super.initState();    _initCache();    countStreamSubscription = widget.countStream.listen((int count) {      totalCount = count;      print('totalCount = $totalCount');      _initCache();      setState(() {});    });  }  @override  void dispose() {    countStreamSubscription.cancel();    super.dispose();  }  @override  Widget build(BuildContext context) {    //debugPrintBeginframeBanner = true;    //debugPrintEndframeBanner = true;    //print('build');    if (error != null && widget.errorBuilder != null) {      return widget.errorBuilder(context, error);    }    if (totalCount == -1 && widget.waitBuilder != null) {      return widget.waitBuilder(context);    }    if (totalCount == 0 && widget.emptyResultBuilder != null) {      return widget.emptyResultBuilder(context);    }    return ListView.builder(      physics: _LazyListViewPhysics(velocityThreshold: widget.velocityThreshold),      itemCount: max(totalCount, 0),      itemBuilder: (context, index) {        // print('builder $index');        final page = index ~/ widget.pageSize;        final pageResult = map[page];        final value = pageResult?.items?.elementAt(index % widget.pageSize);        if (value != null) {          return widget.itemBuilder(context, index, value);        }        // print('$index ${Scrollable.of(context).position.activity.velocity}');        if (!Scrollable.recommendDeferredLoadingForContext(context)) {          cache.get(page, ifAbsent: _loadPage).then(_reload).catchError(_error);        } else if (!_frameCallbackInProgress) {          _frameCallbackInProgress = true;          SchedulerBinding.instance.scheduleframeCallback((d) => _deferredReload(context));        }        return widget.placeholderBuilder(context, index);      },    );  }  Future<PageResult<T>> _loadPage(int index) async {    print('load $index');    var list = await widget.pageFuture(index);    return PageResult(index, list);  }  void _initCache() {    map = LruMap<int, PageResult<T>>(maximumSize: 512 ~/ widget.pageSize);    cache = MapCache<int, PageResult<T>>(map: map);  }  void _error(dynamic e, StackTrace stackTrace) {    if (widget.errorBuilder == null) {      throw e;    }    if (this.mounted) {      setState(() => error = e);    }  }  void _reload(PageResult<T> value) => _doReload(value.index);  void _deferredReload(BuildContext context) {    print('_deferredReload');    if (!Scrollable.recommendDeferredLoadingForContext(context)) {      _frameCallbackInProgress = false;      _doReload(-1);    } else {      SchedulerBinding.instance.scheduleframeCallback((d) => _deferredReload(context), rescheduling: true);    }  }  void _doReload(int index) {    print('reload $index');    if (this.mounted) {      setState(() {});    }  }}class PageResult<T> {  /// Page index of this data.  final int index;  final List<T> items;  PageResult(this.index, this.items);}class _LazyListViewPhysics extends AlwaysScrollableScrollPhysics {  final double velocityThreshold;  _LazyListViewPhysics({    @required this.velocityThreshold,    ScrollPhysics parent,  }) : super(parent: parent);  @override  recommendDeferredLoading(double velocity, ScrollMetrics metrics, BuildContext context) {    // print('velocityThreshold: $velocityThreshold');    return velocity.abs() > velocityThreshold;  }  @override  _LazyListViewPhysics applyTo(ScrollPhysics ancestor) {    // print('applyTo($ancestor)');    return _LazyListViewPhysics(velocityThreshold: velocityThreshold, parent: buildParent(ancestor));  }}

有更好的主意吗?



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/366278.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号