I want to achieve a complex scrolling behavior in flutter. I assume sliver is the way to go but I could not find a way to solve my problem.
Here is a screen of the content I want to scroll:
The behavior I want is the following:
- First the green and red panel needs to scroll with the title keep centered position
- Second when not more space is available the title goes out of the viewport
- Third the red and green panel say pinned at the top of the viewport and the text only scroll under the “Content” and “Quiz” bar
I should be able to do the sticky green and red panel with SliverPinnedHeader
from the sliver_tools
the title container scale down as well maybe with SliverAppBar or something.
I struggle to find how to start the scrolling of the text in the green panel only because the text is inside Row and Column widgets that are not SliverWidget (inside a SliverToBoxAdapter widget). I need a nested scroll view that is “linked” to the root widget to make is scroll when needed somehow.
Widget build(context) {
Stack(
children: [
Container(
color: FPColors.primaryDarker,
height: 400,
width: double.infinity,
child: SvgPicture.asset(
'assets/background/svg/pattern.svg',
fit: BoxFit.fitHeight,
),
),
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Container(
height: 370,
padding: const EdgeInsets.all(16),
child: Center(
child: Text(
"Title",
style: Theme.of(context).textTheme.titleLarge!.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
)
),
SliverPadding(
padding: const EdgeInsets.only(
top: 0,
left: 16,
right: 16,
),
sliver: SliverCrossAxisGroup(
slivers: [
SliverCrossAxisExpanded(
flex: 3,
sliver: EbpModuleContent(ebpModule),
),
const SliverConstrainedCrossAxis(
maxExtent: 16,
sliver: SliverToBoxAdapter(child: SizedBox.shrink()),
),
SliverCrossAxisExpanded(
flex: 1,
sliver: SliverSticky(
child: Container(
decoration: const BoxDecoration(color: Colors.red),
height: 400,
),
),
),
],
),
),
],
),
],
);
}
class Content extends StatelessWidget {
const Content({super.key});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
child: Column(
children: [
Row(
children: [
const Text('Sommaire'),
const Spacer(),
IconButton(
icon: const Icon(Icons.search),
onPressed: () {},
),
],
),
const Gap(8),
const Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Introduction'),
Gap(8),
Text('Chapter 1'),
Gap(8),
Text('Chapter 2'),
Gap(8),
Text('Chapter 3'),
],
),
],
),
),
const Gap(8),
Expanded(
flex: 3,
child: Column(
children: [
const TabBar([
TabBarItem('Content', Icons.article),
TabBarItem('Quiz', Icons.quiz),
TabBarItem('Commentaire', Icons.comment),
]),
const Gap(8),
SingleChildScrollView(
child: Text(
List.generate(10, (index) => '''
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
''').join(' '),
),
)
],
),
),
],
),
),
);
}
}
Thanks