Skip to content

Commit

Permalink
Imperative push & pop
Browse files Browse the repository at this point in the history
  • Loading branch information
PlugFox committed Dec 7, 2023
1 parent b647fde commit 571df35
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,9 @@ mixin _UsernamePasswordFormStateMixin on State<SignInScreen> {
/// Opens the sign up page in the browser
void signUp(BuildContext context) {
FocusScope.of(context).unfocus();
/* url_launcher.launchUrlString('...').ignore(); */
Octopus.of(context).setState((state) => state..add(Routes.signup.node()));
// url_launcher.launchUrlString('...').ignore();
// Octopus.of(context).setState((state) => state..add(Routes.signup.node()));
Octopus.push(context, Routes.signup);
}

@override
Expand Down
9 changes: 3 additions & 6 deletions example/lib/src/feature/home/widget/home_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,17 @@ class HomeScreen extends StatelessWidget {
ListTile(
title: const Text('Shop'),
subtitle: const Text('Shop description'),
onTap: () => Octopus.of(context)
.setState((state) => state..add(Routes.shop.node())),
onTap: () => Octopus.push(context, Routes.shop),
),
ListTile(
title: const Text('Gallery'),
subtitle: const Text('Gallery description'),
onTap: () => Octopus.of(context)
.setState((state) => state..add(Routes.gallery.node())),
onTap: () => Octopus.push(context, Routes.gallery),
),
ListTile(
title: const Text('Account'),
subtitle: const Text('Account description'),
onTap: () => Octopus.of(context)
.setState((state) => state..add(Routes.account.node())),
onTap: () => Octopus.push(context, Routes.account),
),
],
),
Expand Down
14 changes: 12 additions & 2 deletions lib/src/controller/octopus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ abstract base class Octopus {
/// Receives the [Octopus] instance from the elements tree.
static Octopus of(BuildContext context) => OctopusNavigator.of(context);

/// Push a new route using the current [BuildContext].
static void push(BuildContext context, OctopusRoute route,
{Map<String, String>? arguments, bool useRootNavigator = false}) =>
OctopusNavigator.push(context, route, arguments: arguments);

/// Pop the current route using the current [BuildContext].
static void pop(BuildContext context) => OctopusNavigator.pop(context);

/// Pop the current route using the current [BuildContext].
static void maybePop(BuildContext context) =>
OctopusNavigator.maybePop(context);

/// A convenient bundle to configure a [Router] widget.
final OctopusConfig config;

Expand All @@ -63,8 +75,6 @@ abstract base class Octopus {
/// For example you can use it to change multiple states at once and
/// combine them into one change.
Future<void> transaction(OctopusState Function(OctopusState state) change);

// TODO(plugfox): push & pop
}

/// {@nodoc}
Expand Down
62 changes: 57 additions & 5 deletions lib/src/widget/navigator.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:octopus/octopus.dart';
import 'package:octopus/src/widget/no_animation_transition_delegate.dart';

Expand All @@ -13,6 +14,7 @@ class OctopusNavigator extends Navigator {
///
/// You can use the [OctopusNavigator.nested] constructor to create a nested
/// navigator.
@internal
const OctopusNavigator({
required Octopus router,
super.pages = const <Page<Object?>>[],
Expand Down Expand Up @@ -44,15 +46,13 @@ class OctopusNavigator extends Navigator {
/// The [key] parameter is used to identify the navigator.
static Widget nested({
required OctopusRoute defaultRoute,
String? bucket,
TransitionDelegate<Object?>? transitionDelegate,
List<NavigatorObserver> observers = const <NavigatorObserver>[],
String? restorationScopeId,
Key? key,
}) =>
_OctopusNestedNavigatorBuilder(
defaultRoute: defaultRoute,
bucket: bucket,
transitionDelegate: transitionDelegate,
observers: observers,
restorationScopeId: restorationScopeId,
Expand Down Expand Up @@ -88,6 +88,37 @@ class OctopusNavigator extends Navigator {
/// Receives the [Octopus] instance from the elements tree.
static Octopus of(BuildContext context) => maybeOf(context) ?? _notFound();

/// Push a new route.
static void push(BuildContext context, OctopusRoute route,
{Map<String, String>? arguments, bool useRootNavigator = false}) {
if (!useRootNavigator) {
var pushed = false;
context.visitAncestorElements((element) {
if (element
case StatefulElement(
state: _ImperativeNestedNavigatorStateMixin state,
)) {
state.pushRoute(route, arguments);
pushed = true;
return false;
}
return true;
});
if (pushed) return;
}
// Fallback or useRootNavigator is true.
Octopus.maybeOf(context)?.setState(
(state) => state
..add(route.node(arguments: arguments ?? const <String, String>{})),
);
}

/// Try to pop the last route.
static void maybePop(BuildContext context) => Navigator.maybePop(context);

/// Pop the last route.
static void pop(BuildContext context) => Navigator.pop(context);

/// {@nodoc}
final Octopus _router;

Expand Down Expand Up @@ -127,7 +158,6 @@ class _OctopusNavigatorContext extends StatefulElement {
class _OctopusNestedNavigatorBuilder extends StatefulWidget {
const _OctopusNestedNavigatorBuilder({
required this.defaultRoute,
this.bucket,
this.transitionDelegate,
this.observers = const <NavigatorObserver>[],
this.restorationScopeId,
Expand All @@ -136,7 +166,6 @@ class _OctopusNestedNavigatorBuilder extends StatefulWidget {
});

final OctopusRoute defaultRoute;
final String? bucket;
final TransitionDelegate<Object?>? transitionDelegate;
final List<NavigatorObserver> observers;
final String? restorationScopeId;
Expand All @@ -148,7 +177,8 @@ class _OctopusNestedNavigatorBuilder extends StatefulWidget {
}

class _OctopusNestedNavigatorBuilderState
extends State<_OctopusNestedNavigatorBuilder> {
extends State<_OctopusNestedNavigatorBuilder>
with _ImperativeNestedNavigatorStateMixin {
// TODO(plugfox): back button dispatcher
late Octopus _router;
OctopusNode? _parentNode; // Current route node.
Expand Down Expand Up @@ -190,6 +220,22 @@ class _OctopusNestedNavigatorBuilderState
);
}

@override
void pushRoute(OctopusRoute route, [Map<String, String>? arguments]) {
if (!mounted) return;
_router.setState(
(state) {
final parent = state.firstWhereOrNull((node) => node == _parentNode);
parent?.children.add(
route.node(
arguments: arguments ?? const <String, String>{},
),
);
return state;
},
);
}

bool _onPopPage(Route<Object?> route, Object? result) {
if (!route.didPop(result)) return false;
// TODO(plugfox): pop from state
Expand All @@ -216,3 +262,9 @@ class _OctopusNestedNavigatorBuilderState
);
}
}

mixin _ImperativeNestedNavigatorStateMixin<T extends StatefulWidget>
on State<T> {
/// Push a new route.
void pushRoute(OctopusRoute route, [Map<String, String>? arguments]);
}

0 comments on commit 571df35

Please sign in to comment.