Skip to content
This repository was archived by the owner on Feb 25, 2022. It is now read-only.

Add example breadcrumbs #261

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions go_router/example/lib/breadcrumbs.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'shared/data.dart';

void main() => runApp(App());

class App extends StatelessWidget {
App({Key? key}) : super(key: key);

static const title = 'GoRouter Example: Breadcrumbs';

@override
Widget build(BuildContext context) => MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
title: title,
);

late final _router = GoRouter(
routes: [
GoRoute(
path: '/',
name: 'Home',
builder: (context, state) => HomeScreen(families: Families.data),
routes: [
GoRoute(
name: 'Family',
path: 'family/:fid',
builder: (context, state) => FamilyScreen(
family: Families.family(state.params['fid']!),
),
routes: [
GoRoute(
name: 'Person',
path: 'person/:pid',
builder: (context, state) {
final family = Families.family(state.params['fid']!);
final person = family.person(state.params['pid']!);

return PersonScreen(family: family, person: person);
},
),
],
),
],
),
],
navigatorBuilder: (context, child) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
BreadCrumbs(router: _router),
if (child != null)
Expanded(
child: MediaQuery.removePadding(
context: context,
removeTop: true,
child: child,
),
),
],
),
);
}

class BreadCrumbs extends StatelessWidget {
const BreadCrumbs({
required this.router,
Key? key,
}) : super(key: key);

final GoRouter router;

@override
Widget build(BuildContext context) {
// ignore: invalid_use_of_visible_for_testing_member
final matches = router.routerDelegate.matches;
return Container(
height: 100,
alignment: Alignment.centerLeft,
color: Colors.black,
child: SafeArea(
bottom: false,
child: Material(
color: Colors.transparent,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
for (final match in matches) ...[
const Icon(
Icons.arrow_right_outlined,
color: Colors.white,
),
Padding(
padding: const EdgeInsets.all(8),
child: ActionChip(
label: Text(
match.route.name ?? 'Hey',
style: TextStyle(
color: matches.last == match
? Colors.black
: Colors.white,
),
),
backgroundColor:
matches.last == match ? Colors.white : Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
side: const BorderSide(color: Colors.white)),
onPressed: () {
if (matches.last == match) return;
router.navigator!.popUntil(
(route) => route.settings.name == match.route.name,
);
},
),
),
]
].skip(1).toList(),
),
),
),
),
);
}
}

class HomeScreen extends StatelessWidget {
const HomeScreen({required this.families, Key? key}) : super(key: key);
final List<Family> families;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text(App.title)),
body: ListView(
children: [
for (final f in families)
ListTile(
title: Text(f.name),
onTap: () => context.go('/family/${f.id}'),
)
],
),
);
}

class FamilyScreen extends StatelessWidget {
const FamilyScreen({required this.family, Key? key}) : super(key: key);
final Family family;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(family.name)),
body: ListView(
children: [
for (final p in family.people)
ListTile(
title: Text(p.name),
onTap: () => context.go('/family/${family.id}/person/${p.id}'),
),
],
),
);
}

class PersonScreen extends StatelessWidget {
const PersonScreen({required this.family, required this.person, Key? key})
: super(key: key);

final Family family;
final Person person;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: Text(person.name)),
body: Text('${person.name} ${family.name} is ${person.age} years old'),
);
}
164 changes: 164 additions & 0 deletions go_router/example/lib/cupertino_back_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

// ignore: implementation_imports
import 'package:go_router/src/go_route_match.dart';

void main() => runApp(App());

class App extends StatelessWidget {
App({Key? key}) : super(key: key);

static const title = 'GoRouter Example: Cupertino Back Button';

@override
Widget build(BuildContext context) => CupertinoApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
title: title,
localizationsDelegates: const [
DefaultMaterialLocalizations.delegate,
DefaultCupertinoLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
);

final _router = GoRouter(
routes: [
GoRoute(
path: '/',
name: 'Page1',
builder: (context, state) => const Page1Screen(),
routes: [
GoRoute(
path: 'page2',
name: 'Page2',
builder: (context, state) => const Page2Screen(),
routes: [
GoRoute(
path: 'page3',
name: 'Page3',
builder: (context, state) => const Page3Screen(),
),
],
),
],
),
],
);
}

class Page1Screen extends StatelessWidget {
const Page1Screen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) => CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text(App.title),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CupertinoButton(
onPressed: () => context.go('/page2'),
child: const Text('Go to page 2'),
),
],
),
),
);
}

class Page2Screen extends StatelessWidget {
const Page2Screen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) => CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Page2'),
leading: BackButton(),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CupertinoButton(
onPressed: () => context.go('/page2/page3'),
child: const Text('Go to page 3'),
),
],
),
),
);
}

class Page3Screen extends StatelessWidget {
const Page3Screen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) => CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('Page3'),
leading: BackButton(),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text('Hold pressed on the back button to choose route')
],
),
),
);
}

class BackButton extends StatelessWidget {
const BackButton({Key? key}) : super(key: key);

@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: GestureDetector(
onLongPress: () async {
final router = GoRouter.of(context);
final navigator = Navigator.of(context);
// ignore: invalid_use_of_visible_for_testing_member
final matches = router.routerDelegate.matches;
if (matches.isEmpty) return;
final match = await showMenu<GoRouteMatch?>(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
context: context,
position: const RelativeRect.fromLTRB(0, 0, 20, 20),
items: <PopupMenuEntry<GoRouteMatch>>[
for (final match in matches.reversed.skip(1))
PopupMenuItem<GoRouteMatch>(
value: match,
child: Text(match.route.name ?? 'Hey'),
),
],
);
if (match != null) {
// Router go does not animate views that pop
// final location = router.routerDelegate.locationForMatch(match);
// router.go(location);

navigator.popUntil(
(route) => route.settings.name == match.route.name,
);
}
},
child: CupertinoNavigationBarBackButton(
onPressed: () {
Navigator.maybePop(context);
},
),
),
);
}
}
7 changes: 5 additions & 2 deletions go_router/lib/src/go_router_delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,11 @@ class GoRouterDelegate extends RouterDelegate<Uri>
}

/// Get the current location, e.g. /family/f2/person/p1
String get location =>
_addQueryParams(_matches.last.subloc, _matches.last.queryParams);
String get location => locationForMatch(_matches.last);

/// Get the location for a given match, e.g. /family/f2/person/p1
String locationForMatch(GoRouteMatch match) =>
_addQueryParams(match.subloc, match.queryParams);

/// For internal use; visible for testing only.
@visibleForTesting
Expand Down