Basically i have a shellroute builder (go router) where i want to display the appbar
and bottomnavigationbar
on all child routes.
ShellRoute(
navigatorKey: _shellNavigatorKey,
builder: (context, state, child) => DashboardScreen(child: child),
The issue I'm facing is how to make all child routes which are widget.child
on DashboardScreen
scrolling hide/show/float the sliverappbar
?!
class DashboardScreen extends ConsumerStatefulWidget {
const DashboardScreen({Key? key, required this.child}) : super(key: key);
final Widget child;
...
DefaultTabController(
length: 6,
child: Scaffold(
bottomNavigationBar: bottomMenuBuilder(context),
padding: EdgeInsets.zero,
body: NestedScrollView(
floatHeaderSlivers: true,
headerSliverBuilder:
(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
const SliverAppBar(
title: Text("floating appbar"),
centerTitle: false,
automaticallyImplyLeading: false,
floating: true,
actions: [
Icon(Icons.search, size: 30, color: Colors.white),
],
elevation: 0.0,
),
];
},
body: widget.child,
),
),
)
I was facing the same issue and came up with a (dirty) workaround. I simply disable user scrolling for the NestedScrollView and all child scroll views. Then I stack a custom scrollview on top of my ShellRoute and listen for the scroll notification of this scroll view. I use the offset to calculate the offets for NestedScrollView and the child scroll views.
Here is my code:
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
);
}
}
// GoRouter configuration
final _router = GoRouter(
initialLocation: "/home",
routes: [
ShellRoute(
builder: (context, state, child) {
return ShellRoutePage(child: child);
},
routes: [
GoRoute(
path: "/home",
builder: (context, state) {
return MyHomePage();
},
),
],
),
],
);
class ShellRoutePage extends StatelessWidget {
final Widget child;
const ShellRoutePage({super.key, required this.child});
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ShellRoutePageState>(
create: (_) => ShellRoutePageState(),
child: Builder(builder: (context) {
return Stack(
children: [
NestedScrollView(
controller: context.read<ShellRoutePageState>().topController,
physics: const NeverScrollableScrollPhysics(),
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
const SliverAppBar(
pinned: true,
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(
background: ColoredBox(
color: Colors.yellow,
child: Center(
child: Text(
"go_router with nested scroll view and custom scrollview on top of shell route")),
),
),
)
];
},
body: child,
),
NotificationListener<ScrollNotification>(
onNotification: (notification) {
context
.read<ShellRoutePageState>()
.onScroll(notification.metrics.extentBefore);
print(notification.metrics.extentBefore);
return true;
},
child: ListView.builder(
itemBuilder: (context, index) {
return Text("$index");
},
),
),
],
);
}),
);
}
}
class ShellRoutePageState with ChangeNotifier {
ScrollController topController = ScrollController();
ScrollController bottomController = ScrollController();
void onScroll(double offset) {
double topMax = topController.position.maxScrollExtent;
double topOffset = min(topMax, offset);
topController.jumpTo(topOffset);
double bottomOffset = max(0, offset - topMax);
bottomController.jumpTo(bottomOffset);
}
}
class MyHomePage extends StatelessWidget {
final List<Color> _colors = [Colors.red, Colors.blue, Colors.green];
@override
Widget build(BuildContext context) {
return CustomScrollView(
controller: context.read<ShellRoutePageState>().bottomController,
physics: const NeverScrollableScrollPhysics(),
slivers: [
SliverList.builder(
itemBuilder: (context, index) {
return Container(
height: 100,
color: _colors[index % _colors.length],
);
},
)
],
);
}
}
EDIT I filed an issue: https://github.com/flutter/flutter/issues/138209