I have a page with Extended NestedScrollView but I am unable find a way to perform scroll controller action in the nested page. I know this is an unsupported feature for now, so I am looking for any possible solution.
What I have already tried:
- I tried get the
PrimaryScrollControllerin context but it still not work (It perform scroll in all tab). This is the code I tried:
PrimaryScrollController.of(context)
.positions
.first
.jumpTo(PrimaryScrollController.of(context)
.positions
.first
.maxScrollExtent);
I installed extended_nested_scrollview and pull_to_refresh.
flutter pub add extended_nested_scroll_view flutter pub add pull_to_refresh
import 'dart:math';
import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
TabController? _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 4, vsync: this);
}
@override
void dispose() {
_tabController?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ExtendedNestedScrollView(
onlyOneScrollInBody: true,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: const Text('Nested Scroll with Tabs'),
pinned: true,
floating: true,
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'Tab 1'),
Tab(text: 'Tab 2'),
Tab(text: 'Tab 3'),
Tab(text: 'Tab 4'),
],
),
),
];
},
body: TabBarView(
controller: _tabController,
children: [
TabPage(),
TabPage(),
TabPage(),
TabPage(),
],
),
),
);
}
}
class TabPage extends StatefulWidget {
TabPage({super.key});
@override
_TabPageState createState() => _TabPageState();
}
class _TabPageState extends State<TabPage> with AutomaticKeepAliveClientMixin {
late RefreshController refreshController;
late TextEditingController textEditingController;
List<String> listItem = [];
late Color bgColor;
Random ran = Random();
@override
void initState() {
listItem.addAll(List.generate(30, (index) => 'item $index'));
bgColor = Colors.primaries[ran.nextInt(Colors.primaries.length)];
refreshController = RefreshController();
textEditingController = TextEditingController();
super.initState();
}
@override
void dispose() {
refreshController.dispose();
textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context); // This is required for AutomaticKeepAliveClientMixin.
return SafeArea(
top: false,
child: Column(
children: [
Expanded(
child: Stack(
children: [
SmartRefresher(
controller: refreshController,
onRefresh: () {
setState(() {
bgColor = Colors
.primaries[ran.nextInt(Colors.primaries.length)];
refreshController.refreshCompleted();
});
},
onLoading: () {
setState(() {
refreshController.loadComplete();
});
},
child: ListView.builder(
itemCount: listItem.length,
controller: PrimaryScrollController.of(context),
itemBuilder: (BuildContext context, int index) {
return ColoredBox(
color: bgColor,
child: ListTile(
title: Text(listItem[index]),
),
);
},
),
),
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.all(18),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(90),
),
child: Padding(
padding: const EdgeInsets.all(4),
child: IconButton(
icon: const Icon(Icons.keyboard_arrow_down),
color: Colors.blue,
onPressed: () {
/// unable scroll to end of list
PrimaryScrollController.of(context)
.positions
.first
.jumpTo(PrimaryScrollController.of(context)
.positions
.first
.maxScrollExtent);
print(
'debug>>${PrimaryScrollController.of(context).positions}');
},
),
),
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: textEditingController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
suffix: IconButton(
onPressed: () {
setState(() {
listItem.add(textEditingController.value.text);
});
},
icon: const Icon(Icons.send))),
),
)
],
),
);
}
@override
bool get wantKeepAlive => true;
}
How can I perform scroll control in NestedScrollView body and retain scrolling behavior in NestedScrollView.