最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

flutter - How to combine ScrollablePositionedList with Navigator pushpop without states - Stack Overflow

programmeradmin1浏览0评论

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
Add a comment  | 

1 Answer 1

Reset to default 0

Make 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),
  );
}
发布评论

评论列表(0)

  1. 暂无评论