Search code examples
timerdartfluttergesture

How can I send onTab signal from nested stateful widget to main.dart


In my main.dart I have a timer and GestureDetector. The GestureDetector onTap etc. handle the user interaction with _handleUserInteraction(). Every time user tabs the app reset the timer. My problem is I need to send signal the onTab (or similar) from form_a.dart to home.dart.

  • main.dart
  • PageView (with bottomNavigationBar) (home.dart) (with timer)
    • Page 1
      • Page 1 Summary (with ListView) (page_1.dart)
      • ListTile 1 onTab
        • ListView A (a.dart)
          • FormA (form_a.dart)
      • List Tile 2 onTab
      • List Tile 3 onTab
    • Page 2
    • Page 3

How can I send onTab signal from nested stateful widget FormA (**form_a.dart) to home.dart?**

I need to access _timer abd void _handleUserInteraction() function from any widget (as deep as gets) under home.dart

import 'package:flutter/material.dart';
import 'dart:async';
import 'main.dart';
import 'details.dart';


class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  Timer _timer;

  // TODO: 3 - INITIALIZE TIMER
  void _initializeTimer() {
    _timer = Timer.periodic(const Duration(minutes: 5), (__) {
      _logOutUser();
    });
  }

  // TODO: 4 - LOG OUT USER
  void _logOutUser() {
    _timer.cancel();

    Navigator.push(context,
        new MaterialPageRoute(builder: (context) => new MyApp()));

  }

  // TODO: 5 - HANDLE USER INTERACTION
  void _handleUserInteraction([_]) {
    if (!_timer.isActive) {
      return;
    }
    _timer.cancel();
    _initializeTimer();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: _handleUserInteraction,
        onDoubleTap: _handleUserInteraction,
        onLongPress: _handleUserInteraction,
        onScaleEnd: _handleUserInteraction,
        onScaleStart: _handleUserInteraction,
        onScaleUpdate: _handleUserInteraction,
        onTapCancel: _handleUserInteraction,
        onTapDown: _handleUserInteraction,
        onTapUp: _handleUserInteraction,
        child: new Scaffold(
          appBar: AppBar(
            title: Text("HOME PAGE"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'GOTO DETAILS PAGE',
                ),
                new RaisedButton(
                    child: new Text("Details"),
                    onPressed: (){
                      Navigator.push(context,
                          new MaterialPageRoute(builder: (context) => new Details()));
                    }
                )
              ],
            ),
          ),
        ));
  }
}


class LoginState extends InheritedWidget{

  LoginState({Key key, Widget child});

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => true;
}   

Solution

  • One way to do this would be by using the capabilities of InheritedWidget.

    First create your InheritedWidget:

    class MyInheritedWidget extends InheritedWidget {
      const MyInheritedWidget({
        Key key,
        VoidCallBack handleOnTap,
        Widget child,
      }) : assert(color != null),
           assert(child != null),
           super(key: key, child: child);
    
      final VoidCallBack handleOnTap;
    
      static MyInheritedWidget of(BuildContext context) {
        return context.inheritFromWidgetOfExactType(MyInheritedWidget);
      }
    
      @override
      bool updateShouldNotify(MyInheritedWidget old) => handleOnTap != old.handleOnTap;
    }
    

    That widget is usefull to retrieve common data between child Widgets and one of their parent in the hierarchy.

    In your main file, create your handler to match a VoidCallBack signature (or change the signature if you want).

    void _handleUserInteraction() {
      // do you stuff to reset your timer
    }
    

    So you should now wrap your PageView with your InheritedWidget to include it as high in the hierarchy as possible (as close as you main as you can) and give it your handling method

    MyInheritedWidget(child: PageView(), handleOnTap: _handleUserInteraction)
    

    Finally, in your onTap call your InheritedWidget handler:

    onTap: () {MyInheritedWidget.of(context).handleOnTap}