inview_notifier_list icon indicating copy to clipboard operation
inview_notifier_list copied to clipboard

SmartRefresher+inview_notifier_list

Open hxs2mr opened this issue 4 years ago • 17 comments

hxs2mr avatar Jul 17 '20 02:07 hxs2mr

Can you provide more info? Is it a bug?

rvamsikrishna avatar Jul 17 '20 10:07 rvamsikrishna

SmartRefresher+inview_notifier_list : Drop down list refresh failed

gaolong1314 avatar Aug 06 '20 07:08 gaolong1314

@gaolong1314 Do you get any error logs?

rvamsikrishna avatar Aug 06 '20 07:08 rvamsikrishna

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 ?

gaolong1314 avatar Aug 06 '20 09:08 gaolong1314

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(); // }

}

gaolong1314 avatar Aug 07 '20 00:08 gaolong1314

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.

LeGoffMael avatar Mar 18 '21 15:03 LeGoffMael

Do shrinkWrap = true

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.

neelansh-creatorstack avatar May 31 '21 20:05 neelansh-creatorstack

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

sdegenaar avatar Jun 14 '21 05:06 sdegenaar

Same as above

Dove56 avatar Aug 19 '21 10:08 Dove56

Same as above Did you solve it? I met too.

youlai avatar Sep 04 '21 02:09 youlai

https://github.com/peng8350/flutter_pulltorefresh/issues/152 I find this is because of this issue

youlai avatar Sep 04 '21 09:09 youlai

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

youlai avatar Sep 04 '21 09:09 youlai

Does anyone have a solution to this problem?

codercq avatar Dec 08 '21 09:12 codercq

Screenshot 2021-12-08 at 3 16 29 PM

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 avatar Dec 08 '21 09:12 neelansh-creatorstack

@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?

rohankandwal avatar Feb 25 '22 11:02 rohankandwal

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

ddevnam avatar Jul 12 '23 16:07 ddevnam

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?

Jagdish-crossml avatar Dec 01 '23 11:12 Jagdish-crossml