【问题标题】:didChangeDependencies hook in Flutter Widget includes not accurate data of the classFlutter Widget 中的 didChangeDependencies 钩子包含不准确的类数据
【发布时间】:2020-08-19 06:22:00
【问题描述】:

在我下面的代码中,我在 Flutter 中与 LifeCyrles 作斗争,我显然只能在 didChangeDependencies 钩子或模板小部件中更新我在 Provider 中的状态(通过挂在按钮上的事件等)。

好吧,我不介意只有 didChangeDependencies 钩子对我有用,但是当我在前面提到的钩子中的逻辑依赖于某些类属性时,我在类数据的准确性方面遇到了问题。 我得到数据落后一步(因为我猜它是在build hook 之前调用的)。

我无法在构建挂钩中运行此逻辑,因为它包含更改 Provider 中状态的请求。如果我尝试更改那里的状态,我会遇到以下错误:

setState() or markNeedsBuild() called during build.

还是这个

The setter 'lastPage=' was called on null. Receiver: null Tried calling: lastPage=true

我想做的事:我有一个包装器小部件,它包含三个其他小部件:页脚、页眉和 pageViewer。 当我到达最后一页时,我需要通知我的包装器小部件,以便它做出相应的反应并隐藏页眉和页脚。

我将不胜感激这里的任何帮助!

重点代码:

这是问题,必须解决

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ui_flutter/screens/welcome/welcome_bloc.dart';
import 'package:flutter/scheduler.dart';

class _FooterState extends State<Footer> {

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

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
    _welcomeBloc = _welcome;
    // this._detectLastPage();
  }

  @override
  Widget build(BuildContext context) {

    return Container(
      alignment: Alignment.bottomCenter,
      padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          this.stepper,
          this.nextArrow,
        ],
      ),
    );
  }

  _detectLastPage() {
    // Here I've got inaccurate data

    print(this.widget.currentStep);
}
}

我已经尝试过其他一些钩子,比如 Scheduler,但也许我在那里做错了什么。

SchedulerBinding.instance
        .addPostFrameCallback((_) => this._detectLastPage());

它在第一轮集结时只被调用一次,仅此而已。 我在这里缺少一个 Angular 钩子AfterViewInit。在这里会很方便。

Mounted 在 VueJS 中


如果您想查看全貌,这就是我的代码的其余部分。 如果您对架构,结构或其他方面有任何建议,欢迎您。由于我是 Flutter 的新手,因此非常感谢。

ma​​in.dart

import 'package:flutter/material.dart';
import 'package:ui_flutter/routing.dart';
import 'package:provider/provider.dart';
import 'screens/welcome/welcome_bloc.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => WelcomeBloc()),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        initialRoute: '/welcome',
        onGenerateRoute: RouteGenerator.generateRoute,
      ),
    );
  }
}

welcome.dart(我的包装)

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:ui_flutter/screens/welcome/welcome_bloc.dart';
import './footer.dart';
import './viewWrapper.dart';
import './header.dart';
// import 'package:ui_flutter/routing.dart';

class Welcome extends StatefulWidget {
  @override
  _WelcomeState createState() => _WelcomeState();
}

class _WelcomeState extends State<Welcome> {
  WelcomeBloc _welcomeBloc;

  @override
  Widget build(BuildContext context) {
    final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
    this._welcomeBloc = _welcome;
    print('Welcome: _welcome.currentPage - ${this._welcomeBloc.lastPage}');

    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            ViewerWrapper(),
            Footer(
              currentStep: _welcomeBloc.currentPage,
              totalSteps: 3,
              activeColor: Colors.grey[800],
              inactiveColor: Colors.grey[100],
            ),
            WelcomeHeader,
          ],
        ),
      ),
    );
  }
}

welcomeBloc.dart(我的状态通过 Provider)

import 'package:flutter/material.dart';

class WelcomeBloc extends ChangeNotifier {
  PageController _controller = PageController();
  int _currentPage;
  bool _lastPage = false;

  bool get lastPage => _lastPage;

  set lastPage(bool value) {
    _lastPage = value;
    notifyListeners();
  }

  int get currentPage => _currentPage;

  set currentPage(int value) {
    _currentPage = value;
    notifyListeners();
  }

  get controller => _controller;

  nextPage(Duration duration, Curves curve) {
    controller.nextPage(duration: duration, curve: curve);
  }
}

footer.dart(这就是我在代码最底部遇到数据问题的地方 - _detectLastPage 方法)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:ui_flutter/screens/welcome/welcome_bloc.dart';
import 'package:flutter/scheduler.dart';

class Footer extends StatefulWidget {
  final int currentStep;
  final int totalSteps;
  final Color activeColor;
  final Color inactiveColor;
  final Duration duration;
  final Function onFinal;
  final Function onStart;

  Footer({
    this.activeColor,
    this.inactiveColor,
    this.currentStep,
    this.totalSteps,
    this.duration,
    this.onFinal,
    this.onStart,
  }) {}

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

class _FooterState extends State<Footer> {
  final double radius = 10.0;
  final double distance = 4.0;
  Container stepper;
  Container nextArrow;
  bool lastPage;
  WelcomeBloc _welcomeBloc;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    final WelcomeBloc _welcome = Provider.of<WelcomeBloc>(context);
    _welcomeBloc = _welcome;
    this._detectLastPage();
  }

  @override
  Widget build(BuildContext context) {
    this._makeStepper();
    this._makeNextArrow();

    return Container(
      alignment: Alignment.bottomCenter,
      padding: EdgeInsets.symmetric(vertical: 30.0, horizontal: 30.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          this.stepper,
          this.nextArrow,
        ],
      ),
    );
  }

  _makeCirle(activeColor, inactiveColor, position, currentStep) {
    currentStep = currentStep == null ? 0 : currentStep - 1;
    Color color = (position == currentStep) ? activeColor : inactiveColor;

    return Container(
      height: this.radius,
      width: this.radius,
      margin: EdgeInsets.only(left: this.distance, right: this.distance),
      decoration: BoxDecoration(
          color: color,
          border: Border.all(color: activeColor, width: 2.0),
          borderRadius: BorderRadius.circular(50.0)),
    );
  }

  _makeStepper() {
    List<Container> circles = List();

    for (var i = 0; i < widget.totalSteps; i++) {
      circles.add(
        _makeCirle(this.widget.activeColor, this.widget.inactiveColor, i,
            this.widget.currentStep),
      );
    }

    this.stepper = Container(
      child: Row(
        children: circles,
      ),
    );
  }

  _makeNextArrow() {
    this.nextArrow = Container(
      child: Padding(
        padding: const EdgeInsets.only(right: 8.0),
        child: GestureDetector(
            onTap: () {
              _welcomeBloc.controller.nextPage(
                duration: this.widget.duration ?? Duration(milliseconds: 500),
                curve: Curves.easeInOut,
              );
            },
            child: Icon(
              Icons.arrow_forward,
            )),
      ),
    );
  }

  _onLastPage() {
    if (this.widget.onFinal != null) {
      this.widget.onFinal();
    }
  }

  _onFirstPage() {
    if (this.widget.onStart != null) {
      this.widget.onStart();
    }
  }

  _detectLastPage() {
    // Here I've got inaccurate data 

    int currentPage =
        this.widget.currentStep == null ? 1 : this.widget.currentStep;

    if (currentPage == 1 && this.widget.currentStep == null) {
      this._onFirstPage();
    } else if (currentPage == this.widget.totalSteps) {
      print('lastPage detected');
      setState(() {
        this.lastPage = true;
      });
      _welcomeBloc.lastPage = true;
      this._onLastPage();
    } else {
      setState(() {
        this.lastPage = false;
      });
      _welcomeBloc.lastPage = false;
    }
  }
}

提前致谢!

【问题讨论】:

  • 代码太多。你能举个简单的例子吗?
  • @RémiRousselet,请在所有代码下方检查一下。有些人需要整个代码才能看到全貌。你的意思是这样的极简主义吗?
  • @RémiRoussele 对这个问题有什么想法吗?

标签: flutter dart flutter-layout flutter-dependencies


【解决方案1】:

我也是 Flutter 的新手,但我了解了一些有助于我构建一些应用程序的架构模式。

这是我的做法:

创建一个Provider,它在运行时为您保存数据。 (在您的情况下可以是Bloc)。坚持一种架构,不要试图将提供者和集团放在同一个项目中。两者都用于状态管理,仅使用一个将是一种很好的做法。

其次,Register 提供者使用 ChangeNotificationProvider 或任何其他在数据发生更改时重建子小部件的类似工作的小部件。

第三,在widget的build方法中获取当变量provider的值改变时应该改变的provider。这样只有相关的小部件被重绘。

对于您的情况, 如果您想在到达最后一页后隐藏页眉和页脚,您可以声明一个变量,假设在您的提供程序中默认将isLastPage 设置为false。 接下来,用ChangeNotificationListner 包装小部件,即headerfooter

现在,让该小部件根据isLastPage 的值决定它必须做什么,要么隐藏自己,要么显示。

我希望这会有所帮助!

【讨论】:

  • 感谢您的帮助!是的,这就是我这样做的方式,但在我的问题中,我对didChangeDependencies 更加好奇。我可以轻松访问任何其他组件中的准确数据。我只是在提高技能并寻找解决不同任务的方法,以防我将来需要它们。在build 之后,我猜想钩子是一件非常重要的事情(基于我的 Angular 和 Vue 经验)在上面的这种情况下,我将当前页面作为属性传递给那里。我相信这将是使用 Provider 将逻辑封装在不同视图中的好方法。只是简单地学习......
【解决方案2】:

从长远来看,我似乎在 Flutter 中找到了 Mounted 生命周期钩子,它是在 Future.microtask 的帮助下实现的。不像.addPostFrameCallback

SchedulerBinding.instance
        .addPostFrameCallback((_) => this._detectLastPage());

InitState 一样只触发一次(但只在构建执行结束时),Future.microtask 可以放在build 块内,并在每次更改和状态更新后调用。

它没有解决didChangeDependencies钩子中状态不准确的问题,但提供了另一种执行构建后执行的方法。

@Abion47 当前解决方案的功劳

示例

Future.microtask(() => this._detectLastPage());

【讨论】:

    猜你喜欢
    • 2020-04-07
    • 2020-10-19
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    • 2021-11-09
    • 2019-08-18
    • 2021-05-17
    • 1970-01-01
    相关资源
    最近更新 更多