How to make a flutter app scroll both horizontally and vertically - flutter

I'm having a hard time making a flutter screen be scrollable both horizontally and vertically. I can make it work in one direction but fail when I try it in both directions.
Here is a mock app. For background, I'm targeting the web, where the information should be accessible by keyboard & mouse even if it's outside the immediate viewing area.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
scrollDirection: Axis.horizontal,
children: [
ListView(
scrollDirection: Axis.vertical,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: [
for (var i = 0; i < 4; i++)
Container(
child: Text('Header $i'),
width: 300,
height: 100,
color: Colors.greenAccent,
margin: const EdgeInsets.all(20)),
],
),
...[
for (var i = 0; i < 50; i++)
Container(
child: Text('Column $i'),
width: 150,
height: 50,
color: Colors.orange,
margin: const EdgeInsets.all(10),
)
],
],
)
],
)
],
),
);
}
}
Thank you for any suggestions on how to make this work!
Tony

Maybe you can try InteractiveViewer widget and Table widget
InteractiveViewer flutter Official example:
https://github.com/flutter/gallery/blob/master/lib/demos/reference/transformations_demo.dart

Still not exactly what I want, but it's closer and may be good enough.
I added a scroll controller.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late ScrollController _scrollControllerH;
#override
void initState() {
_scrollControllerH = ScrollController();
super.initState();
}
#override
void dispose() {
_scrollControllerH.dispose();
super.dispose();
}
/// scroll only the header independent of the table
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
scrollDirection: Axis.vertical,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Scrollbar(
controller: _scrollControllerH,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
controller: _scrollControllerH,
child: Row(
children: [
for (var i = 0; i < 4; i++)
Container(
child: Text('Header $i'),
width: 300,
height: 100,
color: Colors.greenAccent,
margin: const EdgeInsets.all(20)),
],
),
),
),
...[
for (var i = 0; i < 50; i++)
Container(
child: Text('Column $i'),
width: 150,
height: 50,
color: Colors.orange,
margin: const EdgeInsets.all(10),
)
],
],
)
],
));
}
}

You can make any view scrollable both vertically and horizontally in Flutter using the following snippet (tested and confirmed to work on a Linux app using Flutter 3.0.1):
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: <widget-to-be-scrollable-both-vertically-and-horizontally>,
),
)

Related

How to make nested NestedScrollViews work?

When trying to use the NestedScrollView with a ListView inside a different NestedScrollView Flutter throws a stack overflow error:
════════ Exception caught by widgets library ═══════════════════════════════════
The following StackOverflowError was thrown building PrimaryScrollController(no controller):
Stack Overflow
Here's a minimal-ish code where it happens:
import 'package:flutter/material.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const NestedScrollView1();
}
}
class NestedScrollView1 extends StatelessWidget {
const NestedScrollView1({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: NestedScrollView(
physics: const ClampingScrollPhysics(),
headerSliverBuilder: (_, __) => [
SliverToBoxAdapter(
child: Container(
color: Colors.blue,
height: 100,
),
)
],
body: NestedScrollView2(),
),
);
}
}
class NestedScrollView2 extends StatelessWidget {
final ScrollController scrollController = ScrollController();
NestedScrollView2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return NestedScrollView(
controller: PrimaryScrollController.of(context),
physics: const ClampingScrollPhysics(),
headerSliverBuilder: (ctx, __) => [
SliverToBoxAdapter(
child: Container(
color: Colors.red,
height: 100,
),
),
],
body: const ListOfItems(),
);
}
}
class ListOfItems extends StatelessWidget {
const ListOfItems({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView(
physics: const ClampingScrollPhysics(),
// controller: PrimaryScrollController.of(context),
children: [
Container(color: Colors.green, height: 200),
Container(color: Colors.yellow, height: 200),
Container(color: Colors.green, height: 200),
Container(color: Colors.yellow, height: 200),
Container(color: Colors.green, height: 200),
Container(color: Colors.yellow, height: 200),
Container(color: Colors.green, height: 200),
Container(color: Colors.yellow, height: 200),
],
);
}
}
if you uncomment the controller line in ListView - it throws a stack overflow like this:
════════ Exception caught by widgets library ═══════════════════════════════════
The following StackOverflowError was thrown building ListView(scrollDirection: vertical, _NestedScrollController#81c19(inner, one client, offset 0.0), ClampingScrollPhysics, dependencies: [MediaQuery]):
Stack Overflow
The relevant error-causing widget was
ListView
Thing is on my project I have a page with a TabBarView and one of its sections has a TabBarView of it's own, and I wanted to use the NestedScrollView's to hold the tabs inside headerSliverBuilder's. Is there any way to go around this, without telling designer to reconsider the page UI or building complex custom scroll logic?
Edit: for clarity, adding a draw.io screenshot of the layout I'm trying to achieve (cannot put images right into the posts yet, ugh).
(Forgot to post an answer, better late than never I hope)
I have managed to achieve what I needed in a hack-ish solution from my colleague of just using CustomScrollView with the nested TabBar and ListView inside of SliverFillRemaining:
import 'package:flutter/material.dart';
void main() async {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: OuterTabView(),
);
}
}
class OuterTabView extends StatefulWidget {
const OuterTabView({Key? key}) : super(key: key);
#override
State<OuterTabView> createState() => _OuterTabViewState();
}
class _OuterTabViewState extends State<OuterTabView> with TickerProviderStateMixin {
late TabController _tabControllerOut;
#override
void initState() {
super.initState();
_tabControllerOut = TabController(length: 3, vsync: this);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (_, __) {
return <Widget>[
SliverToBoxAdapter(
child: TabBar(
tabs: const [
SizedBox(height: 40),
SizedBox(height: 40),
SizedBox(height: 40),
],
controller: _tabControllerOut,
),
),
];
},
body: TabBarView(
controller: _tabControllerOut,
children: const [
Tab1WithNestedTabView(),
Tab2(),
Tab3(),
],
),
),
),
);
}
}
class Tab1WithNestedTabView extends StatefulWidget {
const Tab1WithNestedTabView({Key? key}) : super(key: key);
#override
State<Tab1WithNestedTabView> createState() => _Tab1WithNestedTabViewState();
}
class _Tab1WithNestedTabViewState extends State<Tab1WithNestedTabView> with TickerProviderStateMixin {
late TabController _tabControllerIn;
#override
void initState() {
_tabControllerIn = TabController(length: 2, vsync: this);
super.initState();
}
#override
Widget build(BuildContext context) {
return CustomScrollView(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
controller: PrimaryScrollController.of(context),
slivers: [
SliverToBoxAdapter(
child: TabBar(
controller: _tabControllerIn,
tabs: const [
SizedBox(height: 40),
SizedBox(height: 40),
],
),
),
SliverFillRemaining(
child: TabBarView(
controller: _tabControllerIn,
children: const [
ItemList(
color1: Colors.green,
color2: Colors.yellow,
),
ItemList(
color1: Colors.tealAccent,
color2: Colors.black54,
),
],
),
),
],
);
}
}
class Tab2 extends StatelessWidget {
const Tab2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return CustomScrollView(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
controller: PrimaryScrollController.of(context),
slivers: const [
SliverFillRemaining(
child: ItemList(
color1: Colors.blue,
color2: Colors.yellow,
),
),
],
);
}
}
class Tab3 extends StatelessWidget {
const Tab3({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const ItemList(
color1: Colors.deepOrange,
color2: Colors.pinkAccent,
);
}
}
// Sample list
class ItemList extends StatelessWidget {
final Color color1;
final Color color2;
const ItemList({
required this.color1,
required this.color2,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListView.builder(
physics: const ClampingScrollPhysics(),
itemBuilder: (context, index) {
return Container(
alignment: Alignment.center,
color: index.isOdd ? color1 : color2,
height: 200,
child: Text(index.toString()),
);
},
);
}
}
I admit it's not exactly the most graceful way, but worked fine enough for me. If anyone finds a better one - I'll be happy to mark that one as an accepted answer.

Flutter stateful widget doesn't change container's color on condition?

In my app I generate a random number between 1-10 and i try to guess. I use container and text and gesture detector for it. I want containers to change color if i click on the right number which i generated randomly. But I don't know why i does not work i tried to solve but i could not. I used initstate or late variable but did not work. help me?
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key})
: super(
key: key,
);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Random random = Random();
late int guessNumber = random.nextInt(9) + 1;
#override
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My App"),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Row(
children: [
Expanded(child: Numb(1)),
Expanded(child: Numb(2)),
Expanded(child: Numb(3)),
],
),
),
Expanded(
child: Row(
children: [
Expanded(child: Numb(4)),
Expanded(child: Numb(5)),
Expanded(child: Numb(6)),
],
),
),
Expanded(
child: Row(
children: [
Expanded(child: Numb(7)),
Expanded(child: Numb(8)),
Expanded(child: Numb(9)),
],
),
),
],
));
}
Widget Numb(int numb) {
Color? color = Colors.lightGreen;
return GestureDetector(
onTap: () {
setState(() {
if (guessNumber == numb) {
color = Colors.pink;
}
});
},
child: Container(
margin: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: color,
),
child: Center(
child: Text(
numb.toString(),
style: const TextStyle(fontSize: 24),
),
),
),
);
}
}
The issue is color is inside the build method(while Numb(int numb) is inside build method) and keep getting Colors.lightGreen; on every setState. Put it outside the build method. like on
class _HomeScreenState extends State<HomeScreen> {
Random random = Random();
late int guessNumber = random.nextInt(9) + 1;
Color? color = Colors.lightGreen;

How to constraint item position in flutter?

I try to make a list of widget. It look like this:
I know of no such thing as Constraint Layout in flutter. But I need something to position my arrow icon in a fixed position on the right. To put it simple, this is my widget code:
Row(
children:[
SizedBox(),
Column(),//this is all the item on the left
Spacer(),
Expanded(// this is the heart and arrow button
child: Column()
)
]
)
I notice that if my column on the left get too wide, my arrow and heart icon is shifted out of line.
How to put my icon in fixed position to the right?
here try this, You have to wrap middle column with expanded so it will take the maximum space available
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool isChecked = false;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (_, index) => Container(
padding: EdgeInsets.all(15),
margin: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),border: Border.all(width: 1.5)),
child: Row(
children: [
Container(
width: 25,
height: 40,
color: Colors.black,
),
Expanded(
child: Column(children: [
//put your children here
]),
),
//this will be always on right
Column(
children: [
Icon(Icons.heart_broken),
Icon(Icons.chevron_right),
],
)
],
),
),
);
}
}

How do I get rid of this overflowed pixels error in Flutter Web

I have a navigationbar in flutter web and I am getting a render issue. It works fine when I am full screen, but It shows the error once I minimized the page to almost mobile version.
This is my code:
'''
class NavigationBar extends StatefulWidget {
const NavigationBar({Key key}) : super(key: key);
#override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(10, 0, 20, 0),
width: MediaQuery.of(context).size.width,
color: kNavBarBlueBackground,
height: 100,
child: Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 20),
Image.asset('assets/img/image_logo.png'),
Spacer(),
Row(
children: [
_NavBarItem('Inicio'),
SizedBox(width: 35),
_NavBarItem('Agencias'),
'''
This is the error I am getting.
Edited this with more code because user below asked for code where this navigation bar was placed in code:
'''
lass _ViajeroExpresoPageState extends State<ViajeroExpresoPage> {
void openDrawer() {
_drawerKey.currentState.openDrawer();
}
#override
Widget build(BuildContext context) {
return ResponsiveBuilder(
builder: (context, sizingInformation) {
// Check the sizing information here and return your UI
if (sizingInformation.deviceScreenType == DeviceScreenType.desktop) {
return Desktop();
}
if (sizingInformation.deviceScreenType == DeviceScreenType.tablet) {
return Mobile(openDrawer: openDrawer);
}
return Mobile(openDrawer: openDrawer);
},
);
}
}
class Desktop extends StatelessWidget {
final Function openDrawer;
const Desktop({Key key, this.openDrawer}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _drawerKey,
body: Stack(
children: [
ListView(
children: [
Container(
width: MediaQuery.of(context).size.width,
color: Colors.blue,
child: NavigationBar(),
),
'''
try wrapping the listview widget in 'expanded' widget

Flutter web tabbar scroll issue with non primary scrollcontroller

In continuation with question
The solution provided above is good. But hard for me to implement in my project.
Expected results:
I've created two tabs.
In each tab I have SingleChildScrollView wrapped with Scrollbar.
I can not have the primary scrollcontroller in both the tabs, because that throws me exception: "ScrollController attached to multiple scroll views."
For Tab ONE I use primary scrollcontroller, for Tab TWO I created Scrollcontroller and attached it.
Widgets in both the tabs should be scrollabale using keyboard and mouse.
Actual results:
For Tab ONE with primary scrollcontroller I can scroll both by keyboard and dragging scrollbar.
But for Tab TWO with non primary scrollcontroller, I have to scroll only by dragging scrollbar. This tab doesn't respond to keyboard page up /down keys.
When keyboard keys are used in Tab TWO actually contents of tab ONE are getting scrolled.
Check code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: TabExample(),
);
}
}
class TabExample extends StatefulWidget {
const TabExample({Key key}) : super(key: key);
#override
_TabExampleState createState() => _TabExampleState();
}
class _TabExampleState extends State<TabExample> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(icon: Text('Tab ONE')),
Tab(icon: Text('Tab TWO')),
],
),
title: Text('Tabs Demo'),
),
body: TabBarView(
children: [
WidgetC(),
WidgetD(),
],
),
),
);
}
}
class WidgetC extends StatefulWidget {
const WidgetC({Key key}) : super(key: key);
#override
_WidgetCState createState() => _WidgetCState();
}
class _WidgetCState extends State<WidgetC>
with AutomaticKeepAliveClientMixin<WidgetC> {
List<Widget> children;
#override
void initState() {
children = [];
for (int i = 0; i < 20; i++) {
children.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Container(
height: 100,
width: double.infinity,
color: Colors.blue,
child: Center(child: Text('$i')),
),
),
);
}
super.initState();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Scrollbar(
key: PageStorageKey('WidgetC'),
isAlwaysShown: true,
showTrackOnHover: true,
child: SingleChildScrollView(
child: Column(
children: children,
),
),
);
}
#override
bool get wantKeepAlive => true;
}
class WidgetD extends StatefulWidget {
const WidgetD({Key key}) : super(key: key);
#override
_WidgetDState createState() => _WidgetDState();
}
class _WidgetDState extends State<WidgetD>
with AutomaticKeepAliveClientMixin<WidgetD> {
List<Widget> children;
ScrollController _scrollController;
#override
void initState() {
_scrollController = ScrollController();
children = [];
for (int i = 0; i < 20; i++) {
children.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Container(
height: 100,
width: double.infinity,
color: Colors.green,
child: Center(child: Text('$i')),
),
),
);
}
super.initState();
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Scrollbar(
key: PageStorageKey('WidgetD'),
isAlwaysShown: true,
showTrackOnHover: true,
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
children: children,
),
),
);
}
#override
bool get wantKeepAlive => true;
}
This has been accepted as a bug in flutter.
Pl follow for progress here: https://github.com/flutter/flutter/issues/83711
Note for other developers facing same issue.
To overcome the mentioned problem, I changed my design layout. Instead of tabbar view I used Navigationrail widget. This solved my problem.
NavigationRail widget allowed me to attach primary scroll controller to multiple widgets without giving me exception: "ScrollController attached to multiple scroll views."
Sample code.
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _selectedIndex = 0;
WidgetC _widgetC = WidgetC();
WidgetD _widgetD = WidgetD();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('NavigationRail Demo'), centerTitle: true),
body: Row(
children: <Widget>[
NavigationRail(
elevation: 8.0,
selectedIndex: _selectedIndex,
onDestinationSelected: (int index) {
setState(() {
_selectedIndex = index;
});
},
labelType: NavigationRailLabelType.all,
groupAlignment: 0.0,
destinations: const <NavigationRailDestination>[
NavigationRailDestination(
icon: Icon(Icons.favorite_border),
selectedIcon: Icon(Icons.favorite),
label: Text('Tab ONE'),
),
NavigationRailDestination(
icon: Icon(Icons.bookmark_border),
selectedIcon: Icon(Icons.book),
label: Text('Tab TWO'),
),
],
),
const VerticalDivider(thickness: 1, width: 1),
// This is the main content.
Expanded(
child: _getPageAtIndex(_selectedIndex),
)
],
),
);
}
Widget _getPageAtIndex(int index) {
switch (index) {
case 0:
return _widgetC;
case 1:
return _widgetD;
}
return Container();
}
}
class WidgetC extends StatefulWidget {
const WidgetC({Key key}) : super(key: key);
#override
_WidgetCState createState() => _WidgetCState();
}
class _WidgetCState extends State<WidgetC>
with AutomaticKeepAliveClientMixin<WidgetC> {
List<Widget> children;
#override
void initState() {
children = [];
for (int i = 0; i < 20; i++) {
children.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Container(
height: 100,
width: double.infinity,
color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
child: Center(child: Text('$i')),
),
),
);
}
super.initState();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Scrollbar(
key: PageStorageKey('WidgetC'),
isAlwaysShown: true,
showTrackOnHover: true,
child: SingleChildScrollView(
child: Column(
children: children,
),
),
);
}
#override
bool get wantKeepAlive => true;
}
class WidgetD extends StatefulWidget {
const WidgetD({Key key}) : super(key: key);
#override
_WidgetDState createState() => _WidgetDState();
}
class _WidgetDState extends State<WidgetD>
with AutomaticKeepAliveClientMixin<WidgetD> {
List<Widget> children;
// ScrollController _scrollController;
#override
void initState() {
// _scrollController = ScrollController();
children = [];
for (int i = 0; i < 20; i++) {
children.add(
Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Container(
height: 100,
width: double.infinity,
color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
child: Center(child: Text('$i')),
),
),
);
}
super.initState();
}
#override
void dispose() {
// _scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Scrollbar(
key: PageStorageKey('WidgetD'),
isAlwaysShown: true,
showTrackOnHover: true,
// controller: _scrollController,
child: SingleChildScrollView(
// controller: _scrollController,
child: Column(
children: children,
),
),
);
}
#override
bool get wantKeepAlive => true;
}