UI 不更新区块状态变化

UI Not updating on bloc state change

我是第一次使用带冻结包的 bloc 库。我有一个场景,其中显示了来自 API 的 object 列表。现在列表磁贴有一个标记为收藏按钮,单击它会触发一个事件并切换 fav bool 并发出状态。

问题: object 的值已更改,但 UI 未相应更新。

Main.dart


import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_bloc_update/bloc/fakeapi_bloc.dart';

void main() {
  runApp(
    BlocProvider(
      create: (context) => FakeapiBloc(),
      child: const MyApp(),
    ),
  );
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Scaffold(
        body: SafeArea(child: MyHomePage()),
      ),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    context.read<FakeapiBloc>().add(const FakeapiEvent.loadData());
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<FakeapiBloc, FakeapiState>(
      builder: (context, state) {
        return state.map(
          inital: (_) => const Center(
            child: CircularProgressIndicator(),
          ),
          loading: (_) => const Center(
            child: CircularProgressIndicator(),
          ),
          loaded: (state) {
            return ListView.builder(itemBuilder: (ctx, pos) {
              return ListTile(
                title: Text(state.posts[pos].title),
                trailing: IconButton(
                  onPressed: () {
                    context
                        .read<FakeapiBloc>()
                        .add(FakeapiEvent.markedFav(pos: pos));
                  },
                  icon: Icon(state.posts[pos].isFav
                      ? Icons.favorite
                      : Icons.favorite_border_outlined),
                ),
              );
            });
          },
          error: (_) => const Center(
            child: CircularProgressIndicator(),
          ),
        );
      },
    );
  }
}

Post.dart


import 'dart:convert';

List<Post> postFromJson(String str) =>
    List<Post>.from(json.decode(str).map((x) => Post.fromJson(x)));

String postToJson(List<Post> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Post {
  Post({
    required this.userId,
    required this.id,
    required this.title,
    required this.body,
  });

  final int userId;
  final int id;
  final String title;
  final String body;
  bool isFav = false;

  factory Post.fromJson(Map<String, dynamic> json) => Post(
        userId: json["userId"],
        id: json["id"],
        title: json["title"],
        body: json["body"],
      );

  Map<String, dynamic> toJson() => {
        "userId": userId,
        "id": id,
        "title": title,
        "body": body,
      };
}

bloc.dart


import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:freezed_bloc_update/post.dart';
import 'package:http/http.dart' as http;

part 'fakeapi_event.dart';
part 'fakeapi_state.dart';
part 'fakeapi_bloc.freezed.dart';

class FakeapiBloc extends Bloc<FakeapiEvent, FakeapiState> {
  FakeapiBloc() : super(const FakeapiState.inital()) {
    on<LoadData>(
      (event, emit) async {
        try {
          emit(const FakeapiState.loading());
          Uri uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
          final response = await http.get(uri);
          if (response.statusCode == 200) {
            emit(FakeapiState.loaded(posts: postFromJson(response.body)));
          } else {
            emit(const FakeapiState.error(errorMessage: 'Api Call Failed'));
          }
        } catch (err) {
          emit(const FakeapiState.error(errorMessage: 'Api Call Failed'));
        }
      },
    );

    on<MarkedFav>((event, emit) {
      final previousState = state as Loaded;
      previousState.posts[event.pos].isFav =
          !previousState.posts[event.pos].isFav;
      emit(FakeapiState.loaded(posts: [...previousState.posts]));
    });
  }
}

events.dart


part of 'fakeapi_bloc.dart';

@freezed
class FakeapiEvent with _$FakeapiEvent {
  const factory FakeapiEvent.loadData() = LoadData;
  const factory FakeapiEvent.markedFav({required int pos}) = MarkedFav;
}

states.dart


part of 'fakeapi_bloc.dart';

@freezed
class FakeapiState with _$FakeapiState {
  const factory FakeapiState.inital() = Initial;
  const factory FakeapiState.loading() = Loading;
  const factory FakeapiState.loaded({required List<Post> posts}) = Loaded;
  const factory FakeapiState.error({required String errorMessage}) = Error;
}

我所做的一个解决方案是在状态本身中保留一个 bool 变量(在模型 class 之外)并单击 fav 切换 bool 值。这是重新触发 UI 更新。

你的Postclass应该转换成冻结的class