So, I have bigger scroll view that scrolls horizontally, and inside - little box (red color) and smaller scrollview (orange color) that scrolls vertically. There are two scrollbars on the bigger scrollview (1 - for horizontal), and second - for vertical inner.
And the problem - vertical scrollbar doesnt look right, because it can go only like blue arrow shows, and I want it to have either full height of the bigger scrollview, or be right near scrollable vertical part, but then dont hide itself if scrolled in horizontal direction.
Run on dartPad
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: ScrollSizingWidget(),
),
),
);
}
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({
Key? key,
}) : super(key: key);
@override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
final ScrollController _horizontal = ScrollController();
final ScrollController _vertical = ScrollController();
@override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scrollbar(
controller: _vertical,
notificationPredicate: (notification) => notification.depth == 1,
child: Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
child: SizedBox(
height: 500,
width: 1000,
child: Column(
children:[
Container(width: 1000, height: 200, color: Colors.green),
Flexible(
child: SingleChildScrollView(
controller: _vertical,
child: Container(
height: 700,
width: 1000,
color: Colors.yellow,
)
)
),
]
)
),
),
),
);
}
}
CodePudding user response:
I have used your code to reproduce the issue. If I understood your needs right, here is the fix:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
@override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
@override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
@override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: SingleChildScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
child: Column(
children: [
Container(
height: 200,
width: 1000,
color: Colors.red,
),
Container(
height: 1000,
width: 1000,
color: Colors.orange,
),
],
),
),
),
),
),
);
}
Firstly, you say that your main SingleChildScrollView
scrolls horizontally, but your widget tree starts with a Scrollbar
which uses a vertical ScrollController
. So you should create your widgets step by step, as you explained.
Also, since you want to see the vertical Scrollbar
through your main SingleChildScrollView
, I wrapped both(red and orange Containers) with Scrollbar
and SingleChildScrollView
to have the effect you want. Furthermore, I connected these Scrollbar
and SingleChildScrollView
with the same horizontal ScrollController
. So now, not only the orange Container
, but both are scrollable and stick together, not independent.
If you don't want the red Container
being scrolled along with the orange Container
, check this:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
@override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
@override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
@override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Column(
children: [
Container(
height: 200,
width: 1000,
color: Colors.red,
),
Expanded(
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: SingleChildScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
child: Container(
height: 700,
width: 1000,
color: Colors.orange,
),
),
),
),
],
),
),
),
);
}
Lastly, Scrollbar
's position in iOS is a bit buggy because of the notch, etc. So I wrapped your ScrollSizingWidget
with SafeArea
to fix the issue in iOS.
If these answers are not what you expect, please don't hesitate to write.
Edit: After your explanations in the comments below, I have created another fix. I believe CustomScrollView
and Sliver
widgets are fits here perfectly. The red Container
, which you want to stay in its position, should be wrapped with the SliverAppBar
. Lastly, the orange Container
, which you want to be able to scroll vertically, could be wrapped with SliverFixedExtentList
. Please check the code below:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: SafeArea(
child: ScrollSizingWidget(),
),
),
),
);
}
class ScrollSizingWidget extends StatefulWidget {
const ScrollSizingWidget({Key? key}) : super(key: key);
@override
State<ScrollSizingWidget> createState() => _ScrollSizingWidgetState();
}
class _ScrollSizingWidgetState extends State<ScrollSizingWidget> {
late final ScrollController _horizontal;
late final ScrollController _vertical;
@override
void initState() {
super.initState();
_horizontal = ScrollController();
_vertical = ScrollController();
}
@override
void dispose() {
_horizontal.dispose();
_vertical.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scrollbar(
controller: _horizontal,
scrollbarOrientation: ScrollbarOrientation.bottom,
child: SingleChildScrollView(
controller: _horizontal,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
child: SizedBox(
height: 500,
width: 1000,
child: Scrollbar(
controller: _vertical,
scrollbarOrientation: ScrollbarOrientation.right,
child: CustomScrollView(
controller: _vertical,
scrollDirection: Axis.vertical,
slivers: [
SliverAppBar(
toolbarHeight: 200.0,
collapsedHeight: 200.0,
pinned: true,
stretch: true,
elevation: 0.0,
backgroundColor: Colors.transparent,
title: Container(
height: 200.0,
color: Colors.red,
),
titleSpacing: 0,
),
SliverFixedExtentList(
itemExtent: 1200.0,
delegate: SliverChildBuilderDelegate(
(_, __) => Container(
color: Colors.orange,
),
childCount: 1,
),
),
],
),
),
),
),
);
}