I am listening to a PageController to get the position and then syncing it with a ListView. When the PageView is manipulated the ListView is simultaneously manipulated.
Example: https://github.com/Ramotion/cardslider-android
However, after v0.6.0 I get an assertion error that my ScrollController is not attached to any views. This fires every time there is a stream event triggering the .jumpTo()
method. It still works but the assertion error is driving me nuts.
[VERBOSE-2:shell.cc(181)] Dart Error: Unhandled exception:
'package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 169 pos 12: '_positions.isNotEmpty': ScrollController not attached to any scroll views.
#0 _AssertionError._doThrowNew (dart:core/runtime/liberrors_patch.dart:40:39)
#1 _AssertionError._throwNew (dart:core/runtime/liberrors_patch.dart:36:5)
#2 ScrollController.jumpTo (package:flutter/src/widgets/scroll_controller.dart:169:12)
#3 MyTitle.build.<anonymous closure> (file:///Users/lukepighetti/code/when-coin/when_coin_2/lib/screens/rate/widgets/title.dart:19:19)
#4 _RootZone.runUnaryGuarded (dart:async/zone.dart:1314:10)
#5 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
#6 _DelayedData.perform (dart:async/stream_impl.dart:584:14)
#7 _StreamImplEvents.handleNext (dart:async/stream_impl.dart:700:11)
#8 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:660:7)
#9 _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#10 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
How do I use ScrollController.jumpTo()
without running into this exception?
class MyTitle extends StatelessWidget {
final List<Category> categories;
MyTitle({this.categories});
@override
Widget build(BuildContext context) {
final _controller = ScrollController();
double height = 36.0;
// double width = MediaQuery.of(context).size.width * 0.8;
BlocProvider.of(context).page.listen((page) {
_controller.jumpTo(height * page);
});
return Container(
height: height,
// width: width,
child: ListView(
controller: _controller,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
children: categories
.map((c) => _Title(
title: c.title,
index: categories.indexOf(c),
))
.toList(),
),
);
}
}
As the answer above me states, you are using the ScrollController when it is not yet attached to the ListView or other ScrollView. You can check this with the hasClients attribute.
if (_scrollController.hasClients) {
await _scrollController.animateTo(
0.0,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
);
}
check controller has client ant then delay jump:
if (_scrollController.hasClients) {
Future.delayed(Duration(milliseconds: 50), () {
_scrollController?.jumpTo(_scrollController.position.maxScrollExtent);
});
}
The problem is you are re-creating _controller
and subscribes to it on each build
. The controller must be created once as final class-scoped property. Moreover you should use StatefulWidget
to dispose
controller and subsribe to stream in initState
method.
class MyTitle extends StatefullWidget {
MyTitle({this.categories});
final List<Category> categories;
_MyTitleState createState() => _MyTitleState();
}
class _MyTitleState extends State<MyTitle> {
final _controller = ScrollController(); // <--
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
BlocProvider.of(context, listen: false).page.listen((page) {
if(_constoller.hasClients) {
_controller.jumpTo(height * page);
}
});
}
@override
Widget build(BuildContext context) {
double height = 36.0;
// double width = MediaQuery.of(context).size.width * 0.8;
return Container(
height: height,
// width: width,
child: ListView(
controller: _controller,
scrollDirection: Axis.vertical,
physics: NeverScrollableScrollPhysics(),
children: categories
.map((c) => _Title(
title: c.title,
index: categories.indexOf(c),
))
.toList(),
),
);
}
}
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