Closed shahmirzali49 closed 11 months ago
It work for me as shown below.
The alignment
is for the item, and scrollview_observer
will calcuate position from the top of the viewport by default. If you need to scroll the item to the middle of the viewport, you can use the offset callback.
GlobalKey customScrollViewKey = GlobalKey();
CustomScrollView(
key: customScrollViewKey,
...
)
observerController.jumpTo(
index: 20,
alignment: 0.5,
offset: (targetOffset) {
final customScrollViewRenderObj = (customScrollViewKey.currentContext
?.findRenderObject() as RenderBox);
return customScrollViewRenderObj.size.height * 0.5;
},
);
Finally, if you need to initialize the index location of the item, it is recommended that you read this document.
I'm using flutter_hooks package, initial index model is not working, can you help me? when I press snow(ac_unit_outlined) icon at the bottom it's working as expected. I think the issue is related context(sliverListCtx).
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:scrollview_observer/scrollview_observer.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class CustomScrollViewDemoPage extends HookWidget {
const CustomScrollViewDemoPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final sliverListCtx = useState<BuildContext?>(null);
final customScrollViewKey = useMemoized(() => GlobalKey());
final sliverScrollController = useScrollController();
final observerController = useMemoized(
() => SliverObserverController(controller: sliverScrollController)
..initialIndexModelBlock = () => ObserverIndexPositionModel(
index: 5,
sliverContext: sliverListCtx.value,
offset: (targetOffset) {
final customScrollViewRenderObj = (customScrollViewKey.currentContext?.findRenderObject() as RenderBox);
return customScrollViewRenderObj.size.height * 0.5;
},
alignment: 0.5,
),
);
return Scaffold(
appBar: AppBar(title: const Text("CustomScrollView")),
body: SliverViewObserver(
controller: observerController,
child: CustomScrollView(
controller: sliverScrollController,
key: customScrollViewKey,
slivers: [
SuperSliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
sliverListCtx.value ??= ctx;
});
return Container(
height: 300,
color: Colors.black12,
child: Center(
child: Text(
"index -- $index",
style: const TextStyle(
color: Colors.black,
),
),
),
);
},
childCount: 30,
),
),
],
),
sliverContexts: () {
return [
if (sliverListCtx.value != null) sliverListCtx.value!,
];
},
extendedHandleObserve: (context) {
if (context == sliverListCtx.value) {
return ObserverCore.handleListObserve(context: context);
}
return null;
},
),
floatingActionButton: Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: () {
observerController.animateTo(
sliverContext: sliverListCtx.value,
index: 27,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
alignment: 0.5,
offset: (targetOffset) {
final customScrollViewRenderObj =
(customScrollViewKey.currentContext?.findRenderObject() as RenderBox);
return customScrollViewRenderObj.size.height * 0.5;
},
);
},
icon: const Icon(Icons.ac_unit_outlined),
),
],
),
),
);
}
}
by the way without flutter_hooks it's working mostly.
import 'package:flutter/material.dart';
import 'package:scrollview_observer/scrollview_observer.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
class CustomScrollViewDemoPage extends StatefulWidget {
const CustomScrollViewDemoPage({Key? key}) : super(key: key);
@override
State<CustomScrollViewDemoPage> createState() => _CustomScrollViewDemoPageState();
}
class _CustomScrollViewDemoPageState extends State<CustomScrollViewDemoPage> {
BuildContext? _sliverListCtx;
ScrollController scrollController = ScrollController();
late SliverObserverController observerController;
GlobalKey customScrollViewKey = GlobalKey();
@override
void initState() {
super.initState();
observerController = SliverObserverController(controller: scrollController)
..initialIndexModel = ObserverIndexPositionModel(
index: 10,
alignment: 0.5,
offset: (targetOffset) {
final customScrollViewRenderObj = (customScrollViewKey.currentContext?.findRenderObject() as RenderBox);
return customScrollViewRenderObj.size.height * 0.5;
},
);
}
@override
void dispose() {
observerController.controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("CustomScrollView")),
body: SliverViewObserver(
controller: observerController,
child: CustomScrollView(
controller: scrollController,
key: customScrollViewKey,
slivers: [
SuperSliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) {
_sliverListCtx ??= ctx;
return Container(
height: 300,
color: Colors.black12,
child: Center(
child: Text(
"index -- $index",
style: const TextStyle(
color: Colors.black,
),
),
),
);
},
childCount: 30,
),
),
],
),
sliverContexts: () {
return [
if (_sliverListCtx != null) _sliverListCtx!,
];
},
extendedHandleObserve: (context) {
if (context == _sliverListCtx) {
return ObserverCore.handleListObserve(context: context);
}
return null;
},
),
floatingActionButton: Padding(
padding: const EdgeInsets.all(15.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
onPressed: () {
observerController.animateTo(
sliverContext: _sliverListCtx,
index: 27,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
alignment: 0.5,
offset: (targetOffset) {
final customScrollViewRenderObj =
(customScrollViewKey.currentContext?.findRenderObject() as RenderBox);
return customScrollViewRenderObj.size.height * 0.5;
},
);
},
icon: const Icon(Icons.ac_unit_outlined),
),
],
),
),
);
}
}
when I use .jump() in useEffect(like initstate) it's working but when context update.
useEffect(() {
print("sliverListCtx.value ${sliverListCtx.value}");
observerController.jumpTo(
index: 10,
sliverContext: sliverListCtx.value,
offset: (targetOffset) {
final customScrollViewRenderObj = (customScrollViewKey.currentContext?.findRenderObject() as RenderBox);
return customScrollViewRenderObj.size.height * 0.5;
},
alignment: 0.5,
);
return null;
}, [sliverListCtx.value]);
look debug console logs. first context is null, but when sliverListCtx update useEffect(initState) is running again. because I added -> [sliverListCtx.value]
flutter: sliverListCtx.value null
flutter: sliverListCtx.value SuperSliverList(delegate: SliverChildBuilderDelegate#4d016(estimated child count: 30), renderObject: _RenderSuperSliverList#1e4ce relayoutBoundary=up1 NEEDS-PAINT)
The sliverListCtx
only plays a recording role, any widget will not be refreshed because of it, so there is no need to use useState
.
- final sliverListCtx = useState<BuildContext?>(null);
+ BuildContext? sliverListCtx;
final observerController = useMemoized(
() => SliverObserverController(controller: sliverScrollController)
..initialIndexModelBlock = () {
return ObserverIndexPositionModel(
index: 5,
- sliverContext: sliverListCtx.value,
+ sliverContext: sliverListCtx,
...
);
},
);
...
SuperSliverList(
delegate: SliverChildBuilderDelegate(
(ctx, index) {
+ sliverListCtx ??= ctx;
+
+ // WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ // sliverListCtx.value ??= ctx;
+ // });
...
),
),
...
sliverContexts: () {
return [
- if (sliverListCtx.value != null) sliverListCtx.value!,
+ if (sliverListCtx != null) sliverListCtx!,
];
},
...
extendedHandleObserve: (context) {
- if (context == sliverListCtx.value) {
+ if (context == sliverListCtx) {
return ObserverCore.handleListObserve(context: context);
}
return null;
},
...
observerController.animateTo(
- sliverContext: sliverListCtx.value,
+ sliverContext: sliverListCtx,
index: 27,
...
);
thank you for your help and your time, it's working now. if someone uses flutter_hooks maybe it will help. I used: useRef
final sliverListCtx = useRef<BuildContext?>(null);
Version
1.16.5
Platforms
dart
Device Model
all phones
flutter info
How to reproduce?
alignemnt property not working as excepted in jump or animateTo
alignment: 0.5, I except the widget will be in the center.