Flutter Widget 中的 didChangeDependencies 钩子包含不准确的类数据 [英] didChangeDependencies hook in Flutter Widget includes not accurate data of the class

查看:16
本文介绍了Flutter Widget 中的 didChangeDependencies 钩子包含不准确的类数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In my code below, I am struggling with LifeCyrles in Flutter where I can update my State in Provider, APPARENTLY, only in didChangeDependencies hook or in a template widget (via events hung up on buttons or so).

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

Alright, I don't mind that only didChangeDependencies hook works for me BUT when my logic in earlier mentioned hook depends on some class properties I am having problems with the accuracy of the class data. I get data one step behind (since it's called before build hook I guess).

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

I cannot run this logic in the build hook because it includes a request to change the state in Provider. If I try to change the state there I've got either this error:

setState() 或 markNeedsBuild() 在构建期间调用.

还是这个

在 null 上调用了 setter 'lastPage='.接收方:空尝试调用:lastPage=true

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

What I want to do: I've got a wrapper widget which holds three other widgets: footer, header and pageViewer. When I reach the last page I need to notify my wrapper widget about that so it reacts accordingly and hides header and footer.

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

I would appreciate any help here!

重点代码:

这是问题,必须解决

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,但也许我在那里做错了什么.

I have already tried some other hooks like Scheduler but maybe I did something wrong there.

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

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

It's called only once at the first build-up round and that's it. I lack an Angular hook here AfterViewInit. It would be handy here.

Mounted 在 VueJS 中

or Mounted in VueJS

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

That's the rest of my code if you'd like to see the whole picture. If you have any suggestions on the architecture, structure or something else you are welcome. It's highly appreciated since I'm new to 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)

welcomeBloc.dart (my state via 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 方法)

footer.dart (that's where I've problems with data at the very bottom of the code - _detectLastPage method)

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;
    }
  }
}

提前致谢!

推荐答案

我也是 Flutter 新手,但是我了解了一些架构模式,这些模式帮助我构建了一些应用程序.

I am new to flutter as well, But I have learned about a few architecture patterns that have helped me build some apps.

这是我的做法:

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

Create a Provider which holds the data for you in runtime. (It can be a Bloc in your case). Stick to one architecture, don't try to put providers and blocs in the same project. Both are used for state management and only using one would be a great practice.

其次,Register 使用 ChangeNotificationProvider 或任何其他可以在数据更改时重建子小部件的类似工作的小部件.

Second, Register the providers using ChangeNotificationProvider or any other widgets which does a similar job of rebuilding the child widget when a data gets changed.

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

Third, Get the provider in the build method of the widget that is supposed to change when the value of the variable provider changes. This way only the concerned widget is redrawn.

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

For your case, If you want to hide the header and footer once you reach the last page, you can declare a variable, let's say isLastPage set to false by default in your provider. Next, wrap the widget, i.e. header and footer with ChangeNotificationListner

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

Now, let that widget decide what it has to do based on the value of isLastPage, either hide itself or show.

我希望这会有所帮助!

这篇关于Flutter Widget 中的 didChangeDependencies 钩子包含不准确的类数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆