How to check when my widget screen comes to visibi

2020-08-09 08:05发布

In android if an activity is visible onResume is called. What is the equivalent method of onResume in Flutter?

I need the know when my widget screen is visible so I can auto-play a video based on that. I might go to another widget screen an when I come back it should auto-play.

My approach was to play the video in didUpdateWidget but didUpdateWidget is called every-time even the widget screen is not visible.

Note: I'm not asking about didChangeAppLifecycleState from WidgetsBindingObserver as it gives onResume etc callbacks for the app lifecycle not a particular widget screen.

3条回答
狗以群分
2楼-- · 2020-08-09 08:41

It's probably not the simplest and definitely not perfect, but a while back I implemented events like those with routes. Basically, EventRoute<T> is a drop-in replacement for MaterialPageRoute<T> that provides optional callbacks for when the Widget is created, pushed to the foreground, pushed to the background and when it gets popped off.

event_route.dart:

import 'package:flutter/material.dart';

enum RouteState {
  none,
  created,
  foreground,
  background,
  destroyed
}

class EventRoute<T> extends MaterialPageRoute<T> {
  BuildContext _context;
  RouteState _state;
  Function(BuildContext) _onCreateCallback;
  Function(BuildContext) _onForegroundCallback;
  Function(BuildContext) _onBackgroundCallback;
  Function(BuildContext) _onDestroyCallback;

  EventRoute(BuildContext context, {
    builder,
    RouteSettings settings,
    bool maintainState = true,
    bool fullscreenDialog = false,
    Function(BuildContext) onCreate,
    Function(BuildContext) onForeground,
    Function(BuildContext) onBackground,
    Function(BuildContext) onDestroy
  }):
        _context = context,
        _onCreateCallback = onCreate,
        _onForegroundCallback = onForeground,
        _onBackgroundCallback = onBackground,
        _onDestroyCallback = onDestroy,
        _state = RouteState.none,
        super(builder: builder, settings: settings, maintainState: maintainState, fullscreenDialog: fullscreenDialog);


  void get state => _state;

  @override
  void didChangeNext(Route nextRoute) {
    if (nextRoute == null) {
      _onForeground();
    } else {
      _onBackground();
    }
    super.didChangeNext(nextRoute);
  }

  @override
  bool didPop(T result) {
    _onDestroy();
    return super.didPop(result);
  }

  @override
  void didPopNext(Route nextRoute) {
    _onForeground();
    super.didPopNext(nextRoute);
  }

  @override
  TickerFuture didPush() {
    _onCreate();
    return super.didPush();
  }

  @override
  void didReplace(Route oldRoute) {
    _onForeground();
    super.didReplace(oldRoute);
  }

  void _onCreate() {
    if (_state != RouteState.none || _onCreateCallback == null) {
      return;
    }
    _onCreateCallback(_context);
  }

  void _onForeground() {
    if (_state == RouteState.foreground) {
      return;
    }
    _state = RouteState.foreground;
    if (_onForegroundCallback != null) {
      _onForegroundCallback(_context);
    }
  }

  void _onBackground() {
    if (_state == RouteState.background) {
      return;
    }
    _state = RouteState.background;
    if (_onBackgroundCallback != null) {
      _onBackgroundCallback(_context);
    }
  }

  void _onDestroy() {
    if (_state == RouteState.destroyed || _onDestroyCallback == null) {
      return;
    }
    _onDestroyCallback(_context);
  }
}

And then to push your route you do:

Navigator.push(context, EventRoute(context, builder: (context) => YourWidget(context),
      onCreate: (context) => print('create'),
      onForeground: (context) => print('foreground'),
      onBackground: (context) => print('background'),
      onDestroy: (context) => print('destroy')
));

The context is a little icky though...

查看更多
Emotional °昔
3楼-- · 2020-08-09 08:54

I struggled to get a video to pause when not viewing the main screen of my app. I applied this VisibilityDetector and grabbed the visiblePercentage to force a pause or resume:

VisibilityDetector(
    key: Key('visible-video--key-${this.randomkeygenerator}-1'),
    onVisibilityChanged: (visibilityInfo) {
      var visiblePercentage = visibilityInfo.visibleFraction * 100;

      if (visiblePercentage < 1){ //the magic is done here
        if(_video_controller != null) {
          if(disposed_vid == false) {
            _video_controller.pause();
          }
        }

      }else{
        if(_video_controller != null) {
          if(disposed_vid == false) {
            _video_controller.play();
          }
        }
      }
      debugPrint(
          'Widget ${visibilityInfo.key} is ${visiblePercentage}% visible');
    },
    child: VideoPlayer(_video_controller)),


  @override
  void dispose() {
    // If the video is playing, pause it.
    _video_controller .pause();
    _video_controller .dispose();
    disposed_vid = true;
    super.dispose();
  }
查看更多
可以哭但决不认输i
4楼-- · 2020-08-09 09:03

All of the problems are solved.

Put an observer on the navigator from the root of the widget tree (materialappwidget).

If you need more explanation please follow this link: https://api.flutter.dev/flutter/widgets/RouteObserver-class.html

I have implemented in my project and its working great @Sp4Rx

// Register the RouteObserver as a navigation observer.
final RouteObserver<PageRoute> routeObserver = 
  RouteObserver<PageRoute>();
  void main() {
runApp(MaterialApp(
home: Container(),
navigatorObservers: [routeObserver],
 ));
   }

 class RouteAwareWidget extends StatefulWidget {
  State<RouteAwareWidget> createState() => RouteAwareWidgetState();
 }

 // Implement RouteAware in a widget's state and subscribe it to 
  the 
  RouteObserver.
  class RouteAwareWidgetState extends State<RouteAwareWidget> with 
 RouteAware {

@override
void didChangeDependencies() {
  super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context));
}

@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}

@override
void didPush() {
  // Route was pushed onto navigator and is now topmost route.
}

 @override
void didPopNext() {
  // Covering route was popped off the navigator.
 }

@override
Widget build(BuildContext context) => Container();

 } 
查看更多
登录 后发表回答