Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Nested Widget Callback/Async Questions

Tags:

flutter

dart

From a Flutter issue posted by Yueh Chou:

Not sure if the event will bubble up to the parent widget and trigger the animation with all the nested widgets and callbacks.

In the Original files,

Pressing on a grandchild widget(e.g. FlatButton) of a BackWidget it will trigger a ModalDialog call and pressing the button will make an async call. The Flip Effect will take place at the end of a successful async call.

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';


void main() {
  runApp(new MaterialApp(
      home: new Container(color: Colors.white, child: new RootWidget())));
}

class RootWidget extends StatelessWidget {
  FlipWidget mainFlipWidget;

  @override
  Widget build(BuildContext context) {
    mainFlipWidget = new FlipWidget(
      frontWidget: frontWidget,
      backWidget: new BackWidget(
        onPressedButton: () {
          _flipMainButton();
        },
      ),
    );
    return new Center(child:mainFlipWidget);
  }

  _flipMainButton() {
    mainFlipWidget.doTheFlip();
  }
}

class BackWidget extends StatefulWidget {
  BackWidget({
    this.onPressedButton,
  });

  final VoidCallback onPressedButton;
   Widget buttonFlipWidget;
  _BackWidgetState createState() => new _BackWidgetState();
}

class _BackWidgetState extends State<BackWidget> {

  @override
  Widget build(BuildContext context) {
    widget.buttonFlipWidget = new Container(
      height: 180.0,
      color: Colors.grey[200],
      child:new FlatButton(
        onPressed: () {
          triggerCb();
        },
        child: new Text('Press me'),
      ),
    );
    return widget.buttonFlipWidget;
  }

  triggerCb() {
      widget.onPressedButton();
  }
}

class FlipWidget extends StatefulWidget {
  FlipWidget({
    @required this.frontWidget,
    @required this.backWidget,
  });

  final Widget frontWidget;
  final Widget backWidget;
  final _FlipWidgetState _state = new _FlipWidgetState();

  doTheFlip () {
    _state.doTheFlip();
  }

  _FlipWidgetState createState() => _state;
}

class _FlipWidgetState extends State<FlipWidget> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _frontScale;
  Animation<double> _backScale;

  void initState() {
    super.initState();
    _controller = new AnimationController(vsync: this, duration: const Duration(milliseconds: 1500),);
    _frontScale = new Tween(
      begin: 1.0,
      end: 0.0,
    ).animate(new CurvedAnimation(parent: _controller, curve: new Interval(0.0, 0.5, curve: Curves.easeIn),
    ));
    _backScale = new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.5, 1.0, curve: Curves.easeOut),
    );
  }

  @override
  Widget build(BuildContext context) {
//    ThemeData theme = Theme.of(context);
    return new Scaffold(
      body: new Center(
        child: new Stack(
          children: [
            new AnimatedBuilder(
              child: widget.frontWidget,
              animation: _backScale,
              builder: (BuildContext context, Widget child) {
                final Matrix4 transform = new Matrix4.identity()
                  ..scale(_backScale.value, 1.0, 1.0);

                return new Transform(
                  transform: transform,
                  alignment: FractionalOffset.center,
                  child: child,
                );
              },
            ),
            new AnimatedBuilder(
                child: widget.backWidget,
                animation: _frontScale,
                builder: (BuildContext context, Widget child) {
                  final Matrix4 transform = new Matrix4.identity()
                    ..scale(_frontScale.value, 1.0, 1.0);
                  return new Transform(
                    transform: transform,
                    alignment: FractionalOffset.center,
                    child: child,
                  );
                }
            ),
          ],
        ),
      ),
    );
  }

  doTheFlip() {
    setState(() {
      if (_controller.isCompleted || _controller.velocity > 0)
        _controller.reverse();
      else
        _controller.forward();
    });
  }
}

final Widget frontWidget = new Container(
  color: Colors.white,
  height: 180.0,
  child: new Column(
    children: [
      new Text("Front Side of the card")
    ],
  ),
);
like image 778
Collin Jackson Avatar asked Oct 13 '17 15:10

Collin Jackson


Video Answer


1 Answers

Some changes you should make to this code.

  • RootWidget should be stateful. Store the flipped state there.
  • BackWidget should be stateless. All it has to do is notify RootWidget that the button was pressed.
  • FlipWidget should detect changes to flipped using didUpdateWidget.
  • Don't store a State into a member variable of a Widget, and avoid putting mutator methods on your State if you can avoid it... this is often a sign that you need to store the state higher in your widget tree and pass it down with constructor arguments.

Learn more about StatefulWidget and StatelessWidget in the Widget Framework Tour and interactivity tutorial.

enter image description here

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(new MaterialApp(
    home: new Container(
      color: Colors.white,
      child: new RootWidget(),
    ),
  ));
}

class RootWidget extends StatefulWidget {
  @override
  _RootWidgetState createState() => new _RootWidgetState();
}

class _RootWidgetState extends State<RootWidget> {
  bool _flipped = false;

  @override
  Widget build(BuildContext context) {
    return new Center(
      child: new FlipWidget(
        flipped: _flipped,
        frontWidget: new Container(
          color: Colors.white,
          height: 180.0,
          child: new Column(
            children: [
              new Text("Front Side of the card")
            ],
          ),
        ),
        backWidget: new BackWidget(
          onPressedButton: () {
            setState(() {
              _flipped = !_flipped;
            });
          },
        ),
      ),
    );
  }
}

class BackWidget extends StatelessWidget {
  BackWidget({
    this.onPressedButton,
  });
  final VoidCallback onPressedButton;

  @override
  Widget build(BuildContext context) {
    return new Container(
      height: 180.0,
      color: Colors.grey[200],
      child:new FlatButton(
        onPressed: onPressedButton,
        child: new Text('Press me'),
      ),
    );
  }
}

class FlipWidget extends StatefulWidget {
  FlipWidget({
    @required this.frontWidget,
    @required this.backWidget,
    @required this.flipped,
  });

  final Widget frontWidget;
  final Widget backWidget;
  final bool flipped;

  @override
  _FlipWidgetState createState() => new _FlipWidgetState();
}

class _FlipWidgetState extends State<FlipWidget> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _frontScale;
  Animation<double> _backScale;

  @override
  void initState() {
    super.initState();
    _controller = new AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1500)
    )..value = widget.flipped ? 1.0 : 0.0;
    _frontScale = new Tween(
      begin: 1.0,
      end: 0.0,
    ).animate(new CurvedAnimation(parent: _controller, curve: new Interval(0.0, 0.5, curve: Curves.easeIn),
    ));
    _backScale = new CurvedAnimation(
      parent: _controller,
      curve: new Interval(0.5, 1.0, curve: Curves.easeOut),
    );
  }

  @override
  void didUpdateWidget(FlipWidget oldWidget) {
    if (widget.flipped != oldWidget.flipped) {
      if (widget.flipped) {
        _controller.forward();
      } else {
        _controller.reverse();
      }
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Stack(
          children: [
            new AnimatedBuilder(
              child: widget.frontWidget,
              animation: _backScale,
              builder: (BuildContext context, Widget child) {
                final Matrix4 transform = new Matrix4.identity()
                  ..scale(_backScale.value, 1.0, 1.0);

                return new Transform(
                  transform: transform,
                  alignment: FractionalOffset.center,
                  child: child,
                );
              },
            ),
            new AnimatedBuilder(
                child: widget.backWidget,
                animation: _frontScale,
                builder: (BuildContext context, Widget child) {
                  final Matrix4 transform = new Matrix4.identity()
                    ..scale(_frontScale.value, 1.0, 1.0);
                  return new Transform(
                    transform: transform,
                    alignment: FractionalOffset.center,
                    child: child,
                  );
                }
            ),
          ],
        ),
      ),
    );
  }
}
like image 59
Collin Jackson Avatar answered Sep 22 '22 01:09

Collin Jackson