inview_notifier_list
inview_notifier_list copied to clipboard
SmartRefresher+inview_notifier_list
Can you provide more info? Is it a bug?
SmartRefresher+inview_notifier_list : Drop down list refresh failed
@gaolong1314 Do you get any error logs?
Widget videoItem( bool inView, MapList bean, ) { Widget item; if (bean.isSeeVideo) { // inView && item = Container( height: ScreenUtil.screenWidth * 9 / 32.0, width: ScreenUtil.screenWidth / 2.0, child: ChewieListItem( play: inView, playUrl: bean.h5Url, ), ); } else { item = Container( height: ScreenUtil.screenWidth * 9 / 32.0, width: ScreenUtil.screenWidth / 2.0, margin: EdgeInsets.all(10), child: CachedNetworkImage( imageUrl: bean.backgroundImage, fit: BoxFit.fill, ), ); // return Icon(Icons.add_a_photo); } return item; }
Not get any error logs. when "return Icon(Icons.add_a_photo)" is ok. when "return CachedNetworkImage" drop down list refresh failed. The gesture was intercepted ?
import 'package:inview_notifier_list/inview_notifier_list.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:wsflutter/controllers/videoPage/models/video_liveList_model.dart'; import '../views/ChewieListItem.dart'; import '../view_model/videolist_view_model.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:cached_network_image/cached_network_image.dart';
class VideoChildrenController extends StatefulWidget { final String categoryID; VideoChildrenController({Key key, this.categoryID}) : super(key: key);
@override _VideoChildrenControllerState createState() => _VideoChildrenControllerState(); }
class _VideoChildrenControllerState extends State<VideoChildrenController> with AutomaticKeepAliveClientMixin { // List<VideoBean> videolist = []; int clickPosition = 0; final VideoListViewModel notifier = VideoListViewModel();
// 下来刷新控件初始化 RefreshController _refreshController = RefreshController(initialRefresh: true); ScrollController _scrollController = new ScrollController();
// 下拉刷新 void _onRefresh() { print('刷新头部'); if (widget.categoryID == '2') { print('直播界面请求数据'); notifier.loadVideoList(); // _refreshController.refreshCompleted(); setState(() {}); } else { print('刷新直接结束'); _refreshController.refreshCompleted(resetFooterState: true); } }
// 上拉加载 void _getMoreRefresh() { print('上拉加载更多数据'); _refreshController.loadNoData(); // if (widget.categoryID == '2') { // print('直播界面请求数据'); // notifier.loadVideoList(); // }else{ // _refreshController.refreshCompleted(); // } }
@override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => notifier, child: Consumer<VideoListViewModel>( builder: (_, localNotifier, __) { if (localNotifier.endRefresh) { _refreshController.refreshCompleted(resetFooterState: true); } return Stack( fit: StackFit.expand, children: <Widget>[ SmartRefresher( controller: _refreshController, onRefresh: _onRefresh, onLoading: _getMoreRefresh, enablePullDown: true, enablePullUp: true, header: WaterDropHeader( waterDropColor: Colors.lightBlue, refresh: Text('refrshing'), complete: Text('endRefresh'), ), child: NotificationListener( onNotification: (notification) { if (notification is ScrollEndNotification) { print('ScrollEndNotification'); } if (notification is ScrollStartNotification) { print('ScrollStartNotification'); // isTouch = true; } return true; }, child: InViewNotifierList( controller: _scrollController, scrollDirection: Axis.vertical, initialInViewIds: ['0'], isInViewPortCondition: (double deltaTop, double deltaBottom, double viewPortDimension) { return deltaTop < viewPortDimension && deltaBottom > (10); }, itemCount: localNotifier.videoList.length, builder: (BuildContext context, int index) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return InViewNotifierWidget( id: '$index', builder: (BuildContext context, bool isInView, Widget child) { // return Text('$index');// return // videoItem( // isInView, localNotifier.videoList[index]); GestureDetector( child: videoItem( isInView, localNotifier.videoList[index]), onTap: () { print('我被点击了,在列表中'); // itemClick(index); }, ); }, ); }, ); }, ), ), ), ], ); }, ), ); }
// itemClick(int position) { // setState(() { // //获取当前列表滚动的距离 // clickPosition = position; // }); // print('---------- $position ----------$clickPosition');
// VideoBean bean = videolist[position]; // for (int j = 0; j < videolist.length; j++) { // VideoBean videoBean = videolist[j]; // setState(() { // videoBean.isSeeVideo = false; // }); // } // setState(() { // bean.isSeeVideo = true; // }); // }
// 视频cell Widget videoItem( bool inView, MapList bean, ) { Widget item; if (bean.isSeeVideo) { // inView && item = Container( height: ScreenUtil.screenWidth * 9 / 32.0, width: ScreenUtil.screenWidth / 2.0, child: ChewieListItem( play: inView, playUrl: bean.h5Url, ), ); } else { item = Container( height: ScreenUtil.screenWidth * 9 / 32.0, width: ScreenUtil.screenWidth / 2.0, margin: EdgeInsets.all(10), child: CachedNetworkImage( imageUrl: bean.backgroundImage, fit: BoxFit.fill, ), ); // return Icon(Icons.add_a_photo); } return item; }
@override // TODO: implement wantKeepAlive bool get wantKeepAlive => true;
// @override // void dispose() { // _refreshController.dispose(); // _scrollController.dispose(); // super.dispose(); // }
}
i am confirming that the plugin is not working when i put an InViewNotifierCustomScrollView
inside the SmartRefresher
.
There is not any error in log, but both pullUp and pullDown actions are not working. It looks like the gesture is not intercepted.
Do shrinkWrap = true
i am confirming that the plugin is not working when i put an
InViewNotifierCustomScrollView
inside theSmartRefresher
.There is not any error in log, but both pullUp and pullDown actions are not working. It looks like the gesture is not intercepted.
Hi,
If you add in InViewNotifierList shrinkWrap: true, physics: ScrollPhysics(),
it will work but then the isInView does not change :-( I need this working too.
Thanks
Same as above
Same as above Did you solve it? I met too.
https://github.com/peng8350/flutter_pulltorefresh/issues/152 I find this is because of this issue
Hi,
If you add in InViewNotifierList shrinkWrap: true, physics: ScrollPhysics(),
it will work but then the isInView does not change :-( I need this working too.
Thanks
https://github.com/peng8350/flutter_pulltorefresh/issues/152 I find this is because of this issue
Does anyone have a solution to this problem?

If you use the above hierarchy, it works completely fine. One thing here to note is that your Listview.builder
should be a direct child of the SmartRefresher
. If you wrap it with any other widget or extract it to a separate class, isInView
will not work.
@neelansh-creatorstack Above solution doesn't work if we need to have a Appbar too. In that case, either the Appbar doesn't scroll with the list or SmartRefresher doesn't work. Any idea on how to fix it?
import 'package:inview_notifier_list/src/inview_notifier.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'inherited_inview_widget.dart';
import 'inview_state.dart';
///builds a [ListView] and notifies when the widgets are on screen within a provided area.
///
///The constructor takes an [IndexedWidgetBuilder] which builds the children on demand.
///It's just like the [ListView.builder].
class InViewNotifierList extends InViewNotifier {
final RefreshController refreshController;
// if you pass not a sliver,it will throw error
final Widget? footer;
// This bool will affect whether or not to have the function of drop-up load.
final bool enablePullUp;
/// controll whether open the second floor function
final bool enableTwoLevel;
/// This bool will affect whether or not to have the function of drop-down refresh.
final bool enablePullDown;
/// callback when header refresh
///
/// when the callback is happening,you should use [RefreshController]
/// to end refreshing state,else it will keep refreshing state
final VoidCallback? onRefresh;
/// callback when footer loading more data
///
/// when the callback is happening,you should use [RefreshController]
/// to end loading state,else it will keep loading state
final VoidCallback? onLoading;
InViewNotifierList({
Key? key,
int? itemCount,
this.footer,
this.enablePullUp = false,
this.enableTwoLevel = false,
this.enablePullDown = false,
this.onRefresh,
this.onLoading,
required this.refreshController,
required IndexedWidgetBuilder builder,
List<String> initialInViewIds = const [],
double endNotificationOffset = 0.0,
VoidCallback? onListEndReached,
Duration throttleDuration = const Duration(milliseconds: 200),
Axis scrollDirection = Axis.vertical,
required IsInViewPortCondition isInViewPortCondition,
ScrollController? controller,
EdgeInsets? padding,
ScrollPhysics? physics,
bool reverse = false,
bool? primary,
bool shrinkWrap = false,
bool addAutomaticKeepAlives = true,
}) : assert(endNotificationOffset >= 0.0),
super(
key: key,
initialInViewIds: initialInViewIds,
endNotificationOffset: endNotificationOffset,
onListEndReached: onListEndReached,
throttleDuration: throttleDuration,
isInViewPortCondition: isInViewPortCondition,
child: SmartRefresher(
controller: refreshController,
enablePullDown: enablePullDown,
enablePullUp: enablePullUp,
enableTwoLevel: enableTwoLevel,
onLoading: onLoading,
onRefresh: onRefresh,
child: ListView.builder(
padding: padding,
controller: controller,
scrollDirection: scrollDirection,
physics: physics,
reverse: reverse,
primary: primary,
addAutomaticKeepAlives: addAutomaticKeepAlives,
shrinkWrap: shrinkWrap,
itemCount: itemCount,
itemBuilder: builder,
),
),
);
static InViewState? of(BuildContext context) {
final InheritedInViewWidget widget = context
.getElementForInheritedWidgetOfExactType<InheritedInViewWidget>()!
.widget as InheritedInViewWidget;
return widget.inViewState;
}
}
///builds a [CustomScrollView] and notifies when the widgets are on screen within a provided area.
///
///A [CustomScrollView] lets you supply [slivers] directly to create various scrolling effects,
///such as lists, grids, and expanding headers. For example, to create a scroll view
///that contains an expanding app bar followed by a list and a grid, use a list of
///three slivers: [SliverAppBar], [SliverList], and [SliverGrid].
class InViewNotifierCustomScrollView extends InViewNotifier {
final RefreshController refreshController;
// if you pass not a sliver,it will throw error
final Widget? footer;
// This bool will affect whether or not to have the function of drop-up load.
final bool enablePullUp;
/// controll whether open the second floor function
final bool enableTwoLevel;
/// This bool will affect whether or not to have the function of drop-down refresh.
final bool enablePullDown;
/// callback when header refresh
///
/// when the callback is happening,you should use [RefreshController]
/// to end refreshing state,else it will keep refreshing state
final VoidCallback? onRefresh;
/// callback when footer loading more data
///
/// when the callback is happening,you should use [RefreshController]
/// to end loading state,else it will keep loading state
final VoidCallback? onLoading;
InViewNotifierCustomScrollView({
Key? key,
required List<Widget> slivers,
required this.refreshController,
this.footer,
this.enablePullUp = false,
this.enableTwoLevel = false,
this.enablePullDown = false,
this.onRefresh,
this.onLoading,
List<String> initialInViewIds = const [],
double endNotificationOffset = 0.0,
VoidCallback? onListEndReached,
Duration throttleDuration = const Duration(milliseconds: 200),
Axis scrollDirection = Axis.vertical,
required IsInViewPortCondition isInViewPortCondition,
ScrollController? controller,
ScrollPhysics? physics,
bool reverse = false,
bool? primary,
bool shrinkWrap = false,
Key? center,
double anchor = 0.0,
}) : super(
key: key,
initialInViewIds: initialInViewIds,
endNotificationOffset: endNotificationOffset,
onListEndReached: onListEndReached,
throttleDuration: throttleDuration,
isInViewPortCondition: isInViewPortCondition,
child: SmartRefresher(
controller: refreshController,
enablePullDown: enablePullDown,
enablePullUp: enablePullUp,
enableTwoLevel: enableTwoLevel,
onLoading: onLoading,
onRefresh: onRefresh,
child: CustomScrollView(
slivers: slivers,
anchor: anchor,
controller: controller,
scrollDirection: scrollDirection,
physics: physics,
reverse: reverse,
primary: primary,
shrinkWrap: shrinkWrap,
center: center,
),
),
);
static InViewState? of(BuildContext context) {
final InheritedInViewWidget widget = context
.getElementForInheritedWidgetOfExactType<InheritedInViewWidget>()!
.widget as InheritedInViewWidget;
return widget.inViewState;
}
}
///The widget that gets notified if it is currently inside the viewport condition
///provided by the [IsInViewPortCondition] condition.
///
///
/// ## Performance optimizations
///
/// If your [builder] function contains a subtree that does not depend on the
/// animation, it's more efficient to build that subtree once instead of
/// rebuilding it on every animation tick.
///
/// If you pass the pre-built subtree as the [child] parameter, the
/// AnimatedBuilder will pass it back to your builder function so that you
/// can incorporate it into your build.
///
/// Using this pre-built child is entirely optional, but can improve
/// performance significantly in some cases and is therefore a good practice.
class InViewNotifierWidget extends StatefulWidget {
///a required String property. This should be unique for every widget
///that wants to get notified.
final String id;
///The function that defines and returns the widget that should be notified
///as inView.
///
///The `isInView` tells whether the returned widget is in view or not.
///
///The child should typically be part of the returned widget tree.
final InViewNotifierWidgetBuilder builder;
///The child widget to pass to the builder.
final Widget? child;
const InViewNotifierWidget({
Key? key,
required this.id,
required this.builder,
this.child,
}) : super(key: key);
@override
_InViewNotifierWidgetState createState() => _InViewNotifierWidgetState();
}
class _InViewNotifierWidgetState extends State<InViewNotifierWidget> {
late final InViewState state;
@override
void initState() {
super.initState();
state = InViewNotifierList.of(context)!;
state.addContext(context: context, id: widget.id);
}
@override
void dispose() {
state.removeContext(context: context);
super.dispose();
}
@override
void didUpdateWidget(InViewNotifierWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.id != widget.id) {
state.removeContext(context: context);
state.addContext(context: context, id: widget.id);
}
}
@override
Widget build(BuildContext context) {
return Container(
child: AnimatedBuilder(
animation: state,
child: widget.child,
builder: (BuildContext context, Widget? child) {
final bool isInView = state.inView(widget.id);
return widget.builder(context, isInView, child);
},
),
);
}
}
///The function that defines and returns the widget that should be notified
///as inView.
///
///The `isInView` tells whether the returned widget is in view or not.
///
///The child should typically be part of the returned widget tree.
typedef InViewNotifierWidgetBuilder = Widget Function(
BuildContext context,
bool isInView,
Widget? child,
);
I cloned and added SmartRefresher as above :D
refresh controller is not working by following the above approach. Getting unbounded vertical height error. Does anyone have any solution with inview notifier list. or how to create a feed of videos in a list view builder with visibility detector package?