Skip to content

Commit

Permalink
Add refreshListenable and sign-in sample
Browse files Browse the repository at this point in the history
  • Loading branch information
johnpryan committed Jun 2, 2022
1 parent 4181c6d commit cf26872
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 11 deletions.
227 changes: 227 additions & 0 deletions example/lib/sign_in.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
// Copyright 2021, the Flutter project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// Sign-in example
/// Done using go_router
import 'package:flutter/material.dart';
import 'package:go_router_prototype/go_router_prototype.dart';

void main() {
runApp(BooksApp());
}

class Credentials {
final String username;
final String password;

Credentials(this.username, this.password);
}

class Authentication extends ChangeNotifier {
bool _signedIn = false;

bool isSignedIn() => _signedIn;

Future<void> signOut() async {
_signedIn = false;
notifyListeners();
}

Future<bool> signIn(String username, String password) async {
_signedIn = true;
notifyListeners();
return true;
}
}

class BooksApp extends StatefulWidget {
@override
State<StatefulWidget> createState() => _BooksAppState();
}

class AppState {
final Authentication auth;

AppState(this.auth);

Future<bool> signIn(String username, String password) async {
var success = await auth.signIn(username, password);
return success;
}

Future<void> signOut() async {
await auth.signOut();
}
}

class _BooksAppState extends State<BooksApp> {
final AppState _appState = AppState(Authentication());

@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationParser: _router.parser,
routerDelegate: _router.delegate,
);
}

late final _router = GoRouter(
refreshListenable: _appState.auth,
routes: [
ShellRoute(
path: '/',
builder: (context, child) => child,
redirect: (state) async {
final signedIn = _appState.auth.isSignedIn();
if (!signedIn) return '/signin';
return null;
},
routes: [
StackedRoute(
path: 'home',
builder: (context) => HomeScreen(
onSignOut: () async {
await _appState.signOut();
},
),
routes: [
StackedRoute(
path: 'books',
builder: (context) => const BooksListScreen(),
),
],
),
StackedRoute(
path: 'signin',
builder: (context) => SignInScreen(
onSignedIn: (credentials) async {
await _appState.signIn(
credentials.username, credentials.password);
RouteState.of(context).goTo('/home');
},
),
),
],
),
],
);
}

class HomeScreen extends StatelessWidget {
final VoidCallback onSignOut;

const HomeScreen({required this.onSignOut});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => RouteState.of(context).goTo('books'),
child: const Text('View my bookshelf'),
),
ElevatedButton(
onPressed: onSignOut,
child: const Text('Sign out'),
),
],
),
),
);
}
}

class SignInScreen extends StatefulWidget {
final ValueChanged<Credentials> onSignedIn;

const SignInScreen({required this.onSignedIn});

@override
_SignInScreenState createState() => _SignInScreenState();
}

class _SignInScreenState extends State<SignInScreen> {
String _username = '';
String _password = '';

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
children: [
TextField(
decoration: const InputDecoration(hintText: 'username (any)'),
onChanged: (s) => _username = s,
),
TextField(
decoration: const InputDecoration(hintText: 'password (any)'),
obscureText: true,
onChanged: (s) => _password = s,
),
ElevatedButton(
onPressed: () =>
widget.onSignedIn(Credentials(_username, _password)),
child: const Text('Sign in'),
),
],
),
),
);
}
}

class BooksListScreen extends StatelessWidget {
const BooksListScreen();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: ListView(
children: const [
ListTile(
title: Text('Stranger in a Strange Land'),
subtitle: Text('Robert A. Heinlein'),
),
ListTile(
title: Text('Foundation'),
subtitle: Text('Isaac Asimov'),
),
ListTile(
title: Text('Fahrenheit 451'),
subtitle: Text('Ray Bradbury'),
),
],
),
);
}
}

class ErrorScreen extends StatelessWidget {
const ErrorScreen(this.error, {Key? key}) : super(key: key);
final Exception? error;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Page Not Found')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(error?.toString() ?? 'page not found'),
TextButton(
onPressed: () => RouteState.of(context).goTo('/'),
child: const Text('Home'),
),
],
),
),
);
}
14 changes: 7 additions & 7 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
go_router_prototype:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.1"
js:
dependency: transitive
description:
Expand Down Expand Up @@ -203,13 +210,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.9"
tree_router:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.1"
url_launcher:
dependency: "direct main"
description:
Expand Down
6 changes: 5 additions & 1 deletion lib/go_router_prototype.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

library tree_router;

import 'package:flutter/foundation.dart';

import 'src/delegate.dart';
import 'src/parser.dart';
import 'src/route.dart';
Expand All @@ -18,6 +20,8 @@ class GoRouter {

GoRouter({
required List<RouteBase> routes,
}) : delegate = GoRouterDelegate(routes),
Listenable? refreshListenable,
}) : delegate =
GoRouterDelegate(routes, refreshListenable: refreshListenable),
parser = GoRouteInformationParser();
}
19 changes: 16 additions & 3 deletions lib/src/delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,27 @@ import 'state.dart';
class GoRouterDelegate extends RouterDelegate<Uri>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<Uri> {
late final GlobalRouteState _globalRouteState;
final Listenable? refreshListenable;

factory GoRouterDelegate(List<RouteBase> routes) {
factory GoRouterDelegate(
List<RouteBase> routes, {
Listenable? refreshListenable,
}) {
final state = GlobalRouteState(routes);
return GoRouterDelegate.withState(state);
return GoRouterDelegate.withState(
state,
refreshListenable: refreshListenable,
);
}

GoRouterDelegate.withState(this._globalRouteState) {
GoRouterDelegate.withState(
this._globalRouteState, {
this.refreshListenable,
}) {
_globalRouteState.addListener(notifyListeners);
refreshListenable?.addListener(() {
setNewRoutePath(currentConfiguration);
});
}

@override
Expand Down
5 changes: 5 additions & 0 deletions lib/src/match.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ class RouteMatch {
return i;
}
}
// If they are the same length, and no mismatch was found, return the
// length so that redirect loops can be avoided.
if (routes.length == other.routes.length) {
return routes.length;
}
return -1;
}

Expand Down
1 change: 1 addition & 0 deletions test/route_match_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ void main() {
});

test('getMatchingPrefixIndex', () {
expect(match1.getMatchingPrefixIndex(match1), 2);
expect(match1.getMatchingPrefixIndex(match2), 2);
expect(match2.getMatchingPrefixIndex(match3), 2);
expect(match3.getMatchingPrefixIndex(match1), -1);
Expand Down

0 comments on commit cf26872

Please sign in to comment.