I have this application that has a sliverlist and a sliverAppbar. I need to get the current scroll position of each item in the sliverlist onscroll
and determine if it has crossed the sliverAppbar and update the sliverappbar with the Title of the item. Say starting from Item1 once it crosses the SliverAppBar that means I am viewing Item1 content and when Item2 crosses the SliverAppBar, update the SliverAppBar with the title Item2
to mean the user is viewing Item2
content
I am trying to implement this using a NotificationListener<ScrollEndNotification>
but I am stuck at the second NotificationListener that is supposed to emit notifications to the top of the parent
at this line ScrollEndNotification(metrics: , context:context).dispatch(context);
it throws an error that I should provide a metrics
parameter which I don't know what to provide.
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
ScrollEndNotification(metrics: , context:context).dispatch(context);
return AutoScrollTag(
key: ValueKey(index),
controller: controller,
index: index,
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, left: 20.0, right: 20.0),
child: Container(
color: Colors.red,
//height: 120.0
//height: A varying height
),),},); ),
The complete code is
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: NotificationListener<ScrollEndNotification>(
onNotification: (notification) {
if (notification is ScrollEndNotification) {
///Here I need to know what widget index bubbled the notification, its position
///on the screen and its index
//in the list, in order to do further implementation like update the
//SliverAppBar
print('$notification');
return true;
},
child: CustomScrollView(
controller: controller
slivers: <Widget>[
SliverAppBar(
title: Text(title),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
ScrollEndNotification(metrics: , context:context).dispatch(context);
return AutoScrollTag(
key: ValueKey(index),
controller: controller,
index: index,
child: Padding(
padding: const EdgeInsets.only(
top: 30.0, left: 20.0, right: 20.0),
child: Container(
color: Colors.red,
//height: 120.0
//height: A varying height
),),},); ),
Also if you have a better implementation on how I can achieve this do help me out. In short, I need to keep track when an item is scrolled off the screen and get the say index
of it in the Sliverlist. Keeping in mind that the item
has a variable size container that expands according to the number of children in it. It is a common UX pattern in ecommerce apps. Eg Viewing menus as the user scrolls down and updating what menu the user is viewing as the title crosses the screen.
Providing a link to the gist so you get an idea of the complete implementation
I implemented what you want for various height children.
To make a various child's height, give a height randomly.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title = 'Viewing item 0';
Random random = Random();
Map<int, double> itemHeight = {};
int currentHideIndex = 0;
final _mainScrollController = ScrollController();
@override
void initState() {
super.initState();
_mainScrollController.addListener(_onMainScroll);
}
void _onMainScroll() {
int justHiddenIndex =
findIndexJustHidden(_mainScrollController.position.pixels);
print('justHiddenIndex: $justHiddenIndex');
if (currentHideIndex != justHiddenIndex) {
setState(() {
title = 'Viewing item ${justHiddenIndex + 1}';
});
currentHideIndex = justHiddenIndex;
}
}
int findIndexJustHidden(currentPosition) {
int index = -1;
for (var item in itemHeight.entries) {
if (currentPosition > item.value) {
index = item.key;
} else {
if (index != 0) {
return index;
}
}
}
return index;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: CustomScrollView(
controller: _mainScrollController,
slivers: <Widget>[
SliverAppBar(
title: Text(title),
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
double randomHeight;
if (!itemHeight.containsKey(index)) {
randomHeight = (random.nextInt(100) + 40) * 1.0;
print('index: $index, randomHeight: $randomHeight');
double beforeSumHeight =
index == 0 ? 0 : itemHeight[index - 1];
print({
'index': index,
'beforeSumHeight': beforeSumHeight,
'height': randomHeight
});
itemHeight[index] = beforeSumHeight + randomHeight;
} else {
randomHeight = index == 0
? itemHeight[index]
: itemHeight[index] - itemHeight[index - 1];
}
return AutoScrollTag(
key: ValueKey(index),
controller: AutoScrollController(),
index: index,
child: Container(
height: randomHeight,
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.5),
border: Border(
bottom: BorderSide(
color: Color(0XFF000000).withOpacity(0.08),
width: 1.0,
style: BorderStyle.solid,
),
)),
padding: EdgeInsets.only(top: 10, left: 20.0, right: 20.0),
child: Text('$index'),
),
);
},
),
),
],
),
);
}
Widget _buildBody() {
return Container();
}
}
class WidgetSize extends StatefulWidget {
final Widget child;
final Function onChange;
const WidgetSize({
Key key,
@required this.onChange,
@required this.child,
}) : super(key: key);
@override
_WidgetSizeState createState() => _WidgetSizeState();
}
class _WidgetSizeState extends State<WidgetSize> {
@override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With