如何从另一个窗口小部件的构建中更新窗口小部件状态

时间:2018-12-19 03:21:39

标签: dart flutter

我的应用程序有一个包含CustomScrollView和一个Container的Stack。 CustomScrollView具有带扩展头(FlexibleSpaceBar)的SliverAppBar。容器的属性取决于FlexibleSpaceBar的扩展程度。

响应用户手动扩展/折叠SliverAppBar的操作,我很难更新Container属性。

我幼稚的方法是在构建SliverAppBar的FlexibleSpaceBar的过程中确定扩展比例(遵循FlexibleSpaceBar.build中的代码),然后使用setState通知Stack的父级。

但这会导致异常“在构建期间调用setState()或markNeedsBuild()”。

使用FlexibleSpaceBar的构件来构建不相关的小部件的正确方法是什么?

import 'dart:math';

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

class MyDemoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'demo app',
      home: MyHome(),
    );
  }
}

/// Notifies when there has been a change in the expansion or
/// collapsion[!] of the FlexibleSpace in the SliverAppBar
class FlexibleSpaceBarChangeNotification extends Notification {
  final double collapsedFraction;
  FlexibleSpaceBarChangeNotification({
    this.collapsedFraction,
  });
}

class MyHome extends StatefulWidget {
  @override
  _MyHomeState createState() => _MyHomeState();
}

class _MyHomeState extends State<MyHome> {

  double _headerCollapsedFraction = 0.5;

  @override
  Widget build(BuildContext context) {
    return NotificationListener<FlexibleSpaceBarChangeNotification>(
      onNotification: (FlexibleSpaceBarChangeNotification notification) {
        // This notification occurs when the SliverAppBar is expanded/contracted
        setState(() {
          _headerCollapsedFraction = notification.collapsedFraction;
        });
        return true;
      },
      child: Stack(
        children: <Widget>[
          Material(
            child: CustomScrollView(
              slivers: <Widget>[
                SliverAppBar(
                  pinned: true,
                  expandedHeight: 200.0,
                  flexibleSpace: FlexibleSpaceBar(
                    title: MyFlexibleSpaceBarTitle(
                      // The MyFlexibleSpaceBarTitle widget
                      child: Text('A List of Items'),
                    ),
                  ),
                ),
                SliverList(
                  delegate: SliverChildBuilderDelegate((context, i) =>
                      ListTile(
                        title: Text('List tile #$i'),
                      ),
                    childCount: 50,
                  ),
                ),
              ],
            ),
          ),
          Container(
            width: 100.0,
            height: _headerCollapsedFraction * 100.0,
            color: Colors.pink,
          ),
        ],
      ),
    );
  }
}

class MyFlexibleSpaceBarTitle extends StatefulWidget {
  final Widget child;

  MyFlexibleSpaceBarTitle({
    this.child,
  });

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

class _MyFlexibleSpaceBarTitleState extends State<MyFlexibleSpaceBarTitle> {
  @override
  Widget build(BuildContext context) {

    // Arithmetic mostly derived from FlexibleSpaceBar.build()
    final FlexibleSpaceBarSettings settings = context.inheritFromWidgetOfExactType(FlexibleSpaceBarSettings);
    assert(settings != null, 'No FlexibleSpaceBarSettings found');
    final double deltaExtent = settings.maxExtent - settings.minExtent;
    // 0.0 -> Expanded
    // 1.0 -> Collapsed to toolbar
    final double t = (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0);
    final double fadeStart = max(0.0, 1.0 - kToolbarHeight / deltaExtent);
    const double fadeEnd = 1.0;
    assert(fadeStart <= fadeEnd);
    final double opacity = Interval(fadeStart, fadeEnd).transform(t);

    // This is probably wrong ?
    FlexibleSpaceBarChangeNotification(collapsedFraction: t)..dispatch(context);

    return Opacity(
      opacity: opacity,
      child: widget.child,
    );
  }
}

1 个答案:

答案 0 :(得分:0)

您可以使用 provider package 实现状态管理。这应该使您能够从应用程序的任何位置更新数据。您可以查看此 guide 了解更多详情。