繁体   English   中英

Flutter 块在热重载后不工作

[英]Flutter bloc not working after hot reload

我的应用程序使用 bloc/cubit 显示 Todo 项目列表,在我热重载/热重启应用程序之前工作正常!

我有两个按钮,当我单击这些按钮时,cubit state 被设置为有 3 个待办事项。 此外,我有两个周期性计时器,它再次将 cubit state 设置为只有 1 或 0 个待办事项。 所以项目的数量不断地从 1 变为 0 直到,或者如果我按下一个按钮它瞬间变成 3。

这在热重载/重启之前工作正常,之后按钮不再工作。 但是,定期更改确实有效。 我只能通过在我的“MyApp”基本小部件中创建我的 ToDoBloc 作为字段初始化器来缓解这个问题。

主要.dart:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:todo/todo_api_controller.dart';
import 'package:todo/todo_bloc.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);
  late TodoBloc _todoBloc; //!!----IF I CREATE THE TODOBLOC HERE EVERYTHING WORKS--!!
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    _todoBloc = TodoBloc(
      apiController: TodoApiController(),
    );
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(_todoBloc),
    );
  }
}

class MyHomePage extends StatelessWidget {
  TodoBloc _todoBloc;
  MyHomePage(this._todoBloc, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: BlocProvider<TodoBloc>.value(
        value: _todoBloc,
        child: BlocBuilder<TodoBloc, TodoBlocState>(
          builder: (context, state) {
            return Builder(
                builder: (context) => Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        TextButton(
                            onPressed: () => context.read<TodoBloc>().LoadAll(),
                            child: Text(
                              'pressme',
                              style: TextStyle(color: Colors.red),
                            )),
                        ListView.builder(
                            shrinkWrap: true,
                            itemCount: state.todos.length,
                            itemBuilder: (context, index) {
                              var todo = state.todos[index];
                              return CheckboxListTile(
                                value: todo.isFinished,
                                onChanged: (newvalue) {},
                              );
                            }),
                      ],
                    ));
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await _todoBloc.LoadAll();
        },
      ),
    );
  }
}

todo_api_controller.dart

class TodoApiController {
  List<Todo> GetAll() {
    return [
      Todo(id: "asfsdf", name: "this is todo", isFinished: true, finishedOn: DateTime.now()),
      Todo(id: "asfsdf", name: "this is todo", isFinished: true, finishedOn: DateTime.now()),
    ];
  }

  void Delete(String id) {}

  void Update(Todo todo) {}

  Todo Create() {
    return Todo(id: "asdfsdf");
  }
}

class Todo {
  final String id;
  String name;
  bool isFinished;
  DateTime? finishedOn;

  Todo({required String id, String name = "", bool isFinished = false, DateTime? finishedOn = null})
      : id = id,
        name = name,
        isFinished = isFinished,
        finishedOn = finishedOn {}
}

todo_bloc.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:todo/todo_api_controller.dart';

class TodoBloc extends Cubit<TodoBlocState> {
  static int numberOfInstances = 0;
  int myInstance = -1;
  TodoApiController _apiController;

  List<Todo> todos = [];

  TodoBloc({required TodoApiController apiController})
      : _apiController = apiController,
        super(TodoBlocState(todos: [TodoItemState(id: "asdfsdf", name: "sdfsdf", isFinished: true, finishedOn: null)])) {
    numberOfInstances++;
    myInstance = numberOfInstances;
    Timer.periodic(Duration(seconds: 2), (s) => emit(TodoBlocState()));
    Future.delayed(
        Duration(seconds: 1),
        () => Timer.periodic(
            Duration(seconds: 2), (s) => emit(TodoBlocState(todos: [TodoItemState(id: "asdfsdf", name: "sdfsdf", isFinished: true, finishedOn: null)]))));
  }

  Future<void> LoadAll() async {
    /* var newTodos = _apiController.GetAll();
    todos.clear();
    todos.addAll(newTodos);
    var newState = MakeState();
    emit(newState);*/
    emit(TodoBlocState(todos: [
      TodoItemState(id: "asdfsdf", name: "sdfsdf", isFinished: true, finishedOn: null),
      TodoItemState(id: "asdfsdf", name: "sdfsdf", isFinished: true, finishedOn: null),
      TodoItemState(id: "asdfsdf", name: "sdfsdf", isFinished: true, finishedOn: null),
    ]));
  }

  TodoBlocState MakeState() {
    return TodoBlocState(
      todos: todos
          .map((e) => TodoItemState(
                id: e.id,
                finishedOn: e.finishedOn,
                isFinished: e.isFinished,
                name: e.name,
              ))
          .toList(),
    );
  }
}

class TodoBlocState {
  final List<TodoItemState> todos = [];
  TodoBlocState({List<TodoItemState>? todos}) {
    this.todos.addAll(todos ?? []);
  }
}

class TodoItemState {
  final String id;
  final String name;
  final bool isFinished;
  final DateTime? finishedOn;

  TodoItemState({required this.id, required this.name, required this.isFinished, required this.finishedOn});
}

我无法弄清楚这是为什么,尤其是热重启时,因为这应该重置所有应用程序 state。

编辑:问题在热重载(不是热重启)后出现,但不能通过热重启解决

EDIT2:通过向 MyHomePage class 添加 GlobalKey() 来解决问题。虽然我不明白为什么。 谁可以给我解释一下这个?

发生这种情况是因为您正在构建 function 中初始化TodoBloc 。热重载会重建您的小部件,因此它会触发对其build函数的新调用。

您应该将其转换为StatefulWidget并在 initState TodoBloc中初始化您的initState

class MyApp extends StatefulWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late TodoBloc _todoBloc; 

  @override
  void initState() {
    super.initState();
    _todoBloc = TodoBloc(
      apiController: TodoApiController(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(_todoBloc),
    );
  }
}

您真的不需要在小部件树的顶部声明和初始化您的TodoBloc ,然后将它一直向下传递。 BlocProvider创建一个可通过context.read<TodoBloc>()访问的新实例。

您的MyApp可能看起来像这样。

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BlocProvider(
        create: (context) => TodoBloc(apiController: TodoApiController()), // this is your bloc being created and initialized
        child: MyHomePage(),
      ),
    );
  }
}

MyHomePage可以简化。 请注意缺少BlocProvider.valueBuilder 您只需要一个BlocBuilder并且始终可以使用context.read<TodoBloc>()访问TodoBloc的正确实例。

class MyHomePage extends StatelessWidget {
  MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: BlocBuilder<TodoBloc, TodoBlocState>(
        builder: (context, state) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextButton(
                onPressed: () => context.read<TodoBloc>().LoadAll(),
                child: Text(
                  'pressme',
                  style: TextStyle(color: Colors.red),
                ),
              ),
              ListView.builder(
                shrinkWrap: true,
                itemCount: state.todos.length,
                itemBuilder: (context, index) {
                  var todo = state.todos[index];
                  return CheckboxListTile(
                    value: todo.isFinished,
                    onChanged: (newvalue) {},
                  );
                },
              ),
            ],
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          await context.read<TodoBloc>().LoadAll();
        },
      ),
    );
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM