My app combines ListView and Navigator (push and pop) to browse (sub)sections of a document, starting from a user-supplied keyword or a TOC entry (with a TextButton for each section in the document). The same stateless widgets are used by all Navigator routes.
To obtain initial scrolling to a particular item (typically the line of the keyword) on a page, I replaced ListView by the ScrollablePositionedList package (SPL), which provides initialScrollIndex. This works fine for the first route (Scaffold page), but on Navigator.push, the app freezes, without throwing an exception.
A minimal test program using ListView and Navigator works fine, but freezes when ListView is replaced by SPL. I assume this could be related to the ItemScrollController used by SPL.
Thanks for your advice.
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
List<String> data = [for(var i=0; i<100; i++) '-- Item $i --'];
void main() => runApp(MaterialApp(title: 'Test', home: HomePage()));
final ItemScrollController itemScrollController = ItemScrollController();
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
var listAll = [for(var i=0; i<data.length; i++) i]; // list of indexes in data[]
var listPartim = [for(var i=20; i<40; i++) i]; // list of indexes in data[]
int hot = 0; // hotspot item (to highligthed in list)
return Scaffold(
appBar: AppBar(
title: Row(children: [
TextButton(onPressed: () => itemScrollController.scrollTo(index: 20,
duration: Duration(seconds: 1), curve: Curves.easeInOutCubic),
child: Text('Scroll to item 20 (ONLY when using ScrollablePositionedList)')),
TextButton(onPressed: () { Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondPage(list: listPartim, hot: hot, title: '')));},
child: Text('Navigate to page 2')),
],),),
body: MyScroll(listAll, 25), // Item 25 is hotspot
);
}
}
class SecondPage extends StatelessWidget {
final List<int> list; // list of entryId
final int hot; // entryId of hotspot
final String title;
const SecondPage({super.key, required this.list, required this.hot, required this.title});
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(title),),
body: MyScroll(list, hot),
);
}
class MyScroll extends StatelessWidget {
final List<int> list; final int hotItem;
const MyScroll(this.list, this.hotItem, {super.key});
@override
Widget build(BuildContext context) => ScrollablePositionedList.separated(
itemCount: list.length,
initialScrollIndex: hotItem,
itemBuilder: (context, index) => MyItem(index, hotItem),
itemScrollController: itemScrollController,
separatorBuilder: (context, int index) => const Divider(height: 12, thickness: 1),
);
}
class MyItem extends StatelessWidget {
final int i, hot;
const MyItem(this.i, this.hot, {super.key});
@override
Widget build(BuildContext context) => Center(heightFactor: 3, child:
Container(height: 20.0, color: (i == hot) ? Colors.green : Colors.white, child: Text(data[i])));
}
My app combines ListView and Navigator (push and pop) to browse (sub)sections of a document, starting from a user-supplied keyword or a TOC entry (with a TextButton for each section in the document). The same stateless widgets are used by all Navigator routes.
To obtain initial scrolling to a particular item (typically the line of the keyword) on a page, I replaced ListView by the ScrollablePositionedList package (SPL), which provides initialScrollIndex. This works fine for the first route (Scaffold page), but on Navigator.push, the app freezes, without throwing an exception.
A minimal test program using ListView and Navigator works fine, but freezes when ListView is replaced by SPL. I assume this could be related to the ItemScrollController used by SPL.
Thanks for your advice.
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
List<String> data = [for(var i=0; i<100; i++) '-- Item $i --'];
void main() => runApp(MaterialApp(title: 'Test', home: HomePage()));
final ItemScrollController itemScrollController = ItemScrollController();
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
var listAll = [for(var i=0; i<data.length; i++) i]; // list of indexes in data[]
var listPartim = [for(var i=20; i<40; i++) i]; // list of indexes in data[]
int hot = 0; // hotspot item (to highligthed in list)
return Scaffold(
appBar: AppBar(
title: Row(children: [
TextButton(onPressed: () => itemScrollController.scrollTo(index: 20,
duration: Duration(seconds: 1), curve: Curves.easeInOutCubic),
child: Text('Scroll to item 20 (ONLY when using ScrollablePositionedList)')),
TextButton(onPressed: () { Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondPage(list: listPartim, hot: hot, title: '')));},
child: Text('Navigate to page 2')),
],),),
body: MyScroll(listAll, 25), // Item 25 is hotspot
);
}
}
class SecondPage extends StatelessWidget {
final List<int> list; // list of entryId
final int hot; // entryId of hotspot
final String title;
const SecondPage({super.key, required this.list, required this.hot, required this.title});
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(title),),
body: MyScroll(list, hot),
);
}
class MyScroll extends StatelessWidget {
final List<int> list; final int hotItem;
const MyScroll(this.list, this.hotItem, {super.key});
@override
Widget build(BuildContext context) => ScrollablePositionedList.separated(
itemCount: list.length,
initialScrollIndex: hotItem,
itemBuilder: (context, index) => MyItem(index, hotItem),
itemScrollController: itemScrollController,
separatorBuilder: (context, int index) => const Divider(height: 12, thickness: 1),
);
}
class MyItem extends StatelessWidget {
final int i, hot;
const MyItem(this.i, this.hot, {super.key});
@override
Widget build(BuildContext context) => Center(heightFactor: 3, child:
Container(height: 20.0, color: (i == hot) ? Colors.green : Colors.white, child: Text(data[i])));
}
Share
Improve this question
asked Mar 28 at 16:49
PietMPietM
32 bronze badges
1 Answer
Reset to default 0Make the itemScrollController
a parameter to MyScroll
class MyScroll extends StatelessWidget {
final List<int> list; final int hotItem;
final ItemScrollController itemScrollController;
const MyScroll(this.list, this.hotItem, this.itemScrollController, {super.key});
@override
Widget build(BuildContext context) => ScrollablePositionedList.separated(
itemCount: list.length,
initialScrollIndex: hotItem,
itemBuilder: (context, index) => MyItem(index, hotItem),
itemScrollController: itemScrollController,
separatorBuilder: (context, int index) => const Divider(height: 12, thickness: 1),
);
}
Then define itemScrollController for each page right inside the class, pass that to MyScroll
class HomePage extends StatelessWidget {
HomePage({super.key});
final ItemScrollController itemScrollController = ItemScrollController();
@override
Widget build(BuildContext context) {
var listAll = [for(var i=0; i<data.length; i++) i]; // list of indexes in data[]
var listPartim = [for(var i=20; i<40; i++) i]; // list of indexes in data[]
int hot = 0; // hotspot item (to highligthed in list)
return Scaffold(
appBar: AppBar(
title: Row(children: [
Expanded(
child: TextButton(onPressed: () => itemScrollController.scrollTo(index: 20,
duration: Duration(seconds: 1), curve: Curves.easeInOutCubic),
child: Text('Scroll to item 20 (ONLY when using ScrollablePositionedList)')),
),
Expanded(
child: TextButton(onPressed: () { Navigator.push(context,
MaterialPageRoute(builder: (context) => SecondPage(list: listPartim, hot: hot, title: '')));},
child: Text('Navigate to page 2')),
),
],),),
body: MyScroll(listAll, 25, itemScrollController), // Item 25 is hotspot
);
}
}
class SecondPage extends StatelessWidget {
final List<int> list; // list of entryId
final int hot; // entryId of hotspot
final String title;
SecondPage({super.key, required this.list, required this.hot, required this.title});
final ItemScrollController itemScrollController = ItemScrollController();
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(title),),
body: MyScroll(list, hot, itemScrollController),
);
}