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
我是第一次使用带冻结包的 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