Monitor page display hidden

Is the fact that the rotor of the parent page jump page, child pages, after the modification of data back parent page needs to be updated, jump rotor page components hidden too deep, not suitable for monitoring the routing of return do response, before is to improve the provider scope, across the page to update the data, because the parent list page is rendered, and there are many similar page, Seal the provider into a separate component, and the problem is that the current page data update timing is not manageable.

Solution: There is a FocusNode concept in Flutter, such as routes, input fields (which just happen to be a hole), so you can use the FocusNode+WidgetsBinding(with a frame callback) to monitor the display-hide status of the page.

WidgetVisibilityStateMixin class

Gets the focus condition judgment of the current top layer

import 'package:flutter/material.dart';

///Page status show \ Hide
enum VisibilityState { hide.show }
mixin WidgetVisibilityStateMixin<T extends StatefulWidget> on State<T> implements WidgetsBindingObserver {
  late FocusNode _ownFocusNode, _oldFocusNode, _newFocusNode;
  VisibilityState visibilityState = VisibilityState.hide;
  ///List of ignored focuses
  List<FocusNode> _ignoreFocusList = [];

  List<FocusNode> get ignoreFocusList => _ignoreFocusList;

  set ignoreFocusList(List<FocusNode> list) => _ignoreFocusList = list;
  
  ///According to
  void onShow() {
    visibilityState = VisibilityState.show;
  }
  
  ///Don't show
  void onHide() {
    visibilityState = VisibilityState.hide; } _addFocusNodeChangeCb() { _ownFocusNode = _oldFocusNode = _newFocusNode = FocusManager.instance.primaryFocus! ; WidgetsBinding.instance! .addObserver(this); WidgetsBinding.instance! .addPersistentFrameCallback(focusNodeChangeCb); onShow(); }///Focal judgment
  voidfocusNodeChangeCb(_) { _newFocusNode = FocusManager.instance.primaryFocus! ;if (_newFocusNode == _oldFocusNode) return;
    _oldFocusNode = _newFocusNode;
    
    if (_judgeNeedIgnore(_newFocusNode)) return;
    if (_newFocusNode == _ownFocusNode) {
      if(visibilityState ! = VisibilityState.show) { onShow(); }}else {
      if(visibilityState ! = VisibilityState.hide) { onHide(); }}}///Ignore focus value
  bool _judgeNeedIgnore(focusNode) {
    return _ignoreFocusList.contains(focusNode);
  }

  @override
  void initState() {
    super.initState();
    Future(_addFocusNodeChangeCb);
  }

  @override
  voiddispose() { WidgetsBinding.instance! .removeObserver(this);
    super.dispose(); }}Copy the code

Simple to use

The listening page needs to implement WidgetsBindingObserver

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

///The listening page needs to implement WidgetsBindingObserver
class _HomePageState extends State<HomePage> with WidgetVisibilityStateMixin.WidgetsBindingObserver {
  final inputFocus = FocusNode();

  @override
  onHide() {
    super.onHide();
    print('hide');
  }

  @override
  void onShow() {
    super.onShow();
    print('show');
  }

  @override
  void initState() {
    super.initState();
    ///Add TextField focus that you want to ignore
    ignoreFocusList = [inputFocus];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
      ),
      body: ListView(
        children: [
          ListTile(
            title: Text("childPage"),
            onTap: () {
              Navigator.pushNamed(context, '/child'); },),///The input box needs to handle the focus separately and release it in time to avoid affecting the page-level focus acquisition
          TextField(
            focusNode: inputFocus,
          ),
          TextButton(
            onPressed: () {
              inputFocus.unfocus();
            },
            child: Text('Release input box focus test'() [() [(); }}class ChildPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("childPage"),
      ),
      body: Center(
        child: Text(
          "childPage", style: Theme.of(context).textTheme.headline3, ), ), ); }}Copy the code

A complete example

import 'package:flutter/material.dart';

enum VisibilityState { hide.show }

mixin WidgetVisibilityStateMixin<T extends StatefulWidget> on State<T> implements WidgetsBindingObserver {
  late FocusNode _ownFocusNode, _oldFocusNode, _newFocusNode;
  VisibilityState visibilityState = VisibilityState.hide;
  ///List of ignored focuses
  List<FocusNode> _ignoreFocusList = [];

  List<FocusNode> get ignoreFocusList => _ignoreFocusList;

  set ignoreFocusList(List<FocusNode> list) => _ignoreFocusList = list;

  void onShow() {
    visibilityState = VisibilityState.show;
  }

  void onHide() {
    visibilityState = VisibilityState.hide; } _addFocusNodeChangeCb() { _ownFocusNode = _oldFocusNode = _newFocusNode = FocusManager.instance.primaryFocus! ; WidgetsBinding.instance! .addObserver(this); WidgetsBinding.instance! .addPersistentFrameCallback(focusNodeChangeCb); onShow(); }voidfocusNodeChangeCb(_) { _newFocusNode = FocusManager.instance.primaryFocus! ;if (_newFocusNode == _oldFocusNode) return;
    _oldFocusNode = _newFocusNode;

    if (_judgeNeedIgnore(_newFocusNode)) return;
    if (_newFocusNode == _ownFocusNode) {
      ///According to
      if(visibilityState ! = VisibilityState.show) { onShow(); }}else {
      ///Don't show
      if(visibilityState ! = VisibilityState.hide) { onHide(); }}}bool _judgeNeedIgnore(focusNode) {
    return _ignoreFocusList.contains(focusNode);
  }

  @override
  void initState() {
    super.initState();
    Future(_addFocusNodeChangeCb);
  }

  @override
  voiddispose() { WidgetsBinding.instance! .removeObserver(this);
    super.dispose();
  }
}

main() {
  runApp(TestPageOnShowApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        "/": (_) => HomePage(),
        "/child": (_) => ChildPage(),
      },
      // home: HomePage(),
      initialRoute: '/',); }}class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> with WidgetVisibilityStateMixin.WidgetsBindingObserver {
  final inputFocus = FocusNode();

  @override
  onHide() {
    super.onHide();
    print('hide');
  }

  @override
  void onShow() {
    super.onShow();
    print('show');
  }

  @override
  void initState() {
    super.initState();
    ignoreFocusList = [inputFocus];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Home"),
      ),
      body: ListView(
        children: [
          ListTile(
            title: Text("childPage"),
            onTap: () {
              Navigator.pushNamed(context, '/child'); },),///The input box needs to handle the focus separately and release it in time to avoid affecting the page-level focus acquisition
          TextField(
            focusNode: inputFocus,
          ),
          TextButton(
            onPressed: () {
              inputFocus.unfocus();
            },
            child: Text('Release focus'() [() [(); }}class ChildPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("childPage"),
      ),
      body: Center(
        child: Text(
          "childPage", style: Theme.of(context).textTheme.headline3, ), ), ); }}Copy the code