rrousselGit / flutter_hooks

React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.
MIT License
3.12k stars 177 forks source link

Add useRouteAware hook #166

Open changalberto opened 4 years ago

changalberto commented 4 years ago

I'm in need of a RouteAware hook for a HookWidget to observer for route changes and make state updates. This would be a good tutorial/example of what some of use cases would be: https://medium.com/flutter-community/how-to-track-screen-transitions-in-flutter-with-routeobserver-733984a90dea But I'm stuck trying to figure out how to create a hook for it. Some guidance on how to structure this would be appreciated.

rrousselGit commented 4 years ago

You can create a separate class:

class Example extends RouteAware {
  @override
  // Called when the current route has been pushed.
  void didPush() {
    print('didPush');
  }
}

...
final route = ModalRoute.of(context);

useEffect(() {
  final example = Example();
  routeObserver.subscribe(example, route);
  return () => routeObserver.unsubscribe(example);
}, [route, routeObserver]);
AlexandraOlegovna commented 3 years ago

I would suggest getting acquainted with wouter, maybe its API will inspire you.

tim-smart commented 3 years ago

Just in case someone stumbles upon this, here is something I'm using based around the example above.

It is using Option from the dartz package, so you might want to replace that with nullable types.

import 'package:dartz/dartz.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class _RouteCallbacks with RouteAware {
  const _RouteCallbacks({
    this.handleDidPopNext = const None(),
    this.handleDidPush = const None(),
    this.handleDidPop = const None(),
    this.handleDidPushNext = const None(),
  });

  final Option<VoidCallback> handleDidPopNext;
  final Option<VoidCallback> handleDidPush;
  final Option<VoidCallback> handleDidPop;
  final Option<VoidCallback> handleDidPushNext;

  @override
  void didPopNext() {
    handleDidPopNext.map((f) => f());
  }

  @override
  void didPush() {
    handleDidPush.map((f) => f());
  }

  @override
  void didPop() {
    handleDidPop.map((f) => f());
  }

  @override
  void didPushNext() {
    handleDidPushNext.map((f) => f());
  }
}

void useRouteObserver(
  RouteObserver<ModalRoute> routeObserver, {
  Option<VoidCallback> didPopNext = const None(),
  Option<VoidCallback> didPush = const None(),
  Option<VoidCallback> didPop = const None(),
  Option<VoidCallback> didPushNext = const None(),
  List<Object?> keys = const [],
}) {
  final context = useContext();
  final route = ModalRoute.of(context);

  useEffect(() {
    if (route == null) return () {};

    final callbacks = _RouteCallbacks(
      handleDidPop: didPop,
      handleDidPopNext: didPopNext,
      handleDidPush: didPush,
      handleDidPushNext: didPushNext,
    );
    routeObserver.subscribe(callbacks, route);
    return () => routeObserver.unsubscribe(callbacks);
  }, [route, routeObserver, ...keys]);
}