0

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,
          ),
        ),
      )

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.