为啥尽管观察者的状态发生变化,但应用程序重启后 Flutter BloC UI 没有更新?
Posted
技术标签:
【中文标题】为啥尽管观察者的状态发生变化,但应用程序重启后 Flutter BloC UI 没有更新?【英文标题】:Why is Flutter BloC UI not updating after app restart despite state change in observer?为什么尽管观察者的状态发生变化,但应用程序重启后 Flutter BloC UI 没有更新? 【发布时间】:2021-12-13 13:49:52 【问题描述】:我承认有很多与我类似的问题,但我还没有从这些问题中找到令人满意的答案。所以我决定提出自己的问题来说明我的问题。我的程序中有 3 个 BloC,每个都有不同的目的。他们都有类似的问题,因此我将询问其中一个 BloC,希望一种解决方案可以解决所有 BloC。
问题是这样的,如果我刚刚启动应用程序并登录,BloC 将更新 UI。如果我已登录、退出应用程序并重新启动它,Bloc 将不会更新 UI。有问题的 Bloc 称为 DetailpersonilBloc,其中 1 个事件称为 Detail,2 个状态称为 DetailpersonilInitial 和 Loaded。在 Detail 事件中,应发出状态 Loaded。
我在 LoginPage 和 GajiPage 在 initState 调用了 Detail。这在我刚刚打开应用程序时有效,但在我重新启动应用程序时无效。我也有平等的想法,认为它会帮助我,但显然它没有任何改变。
注意:GajiPage 中的“...”只是一些我认为不需要复制的代码。
DetailpersonilBloc
part 'detailpersonil_event.dart';
part 'detailpersonil_state.dart';
class DetailpersonilBloc
extends Bloc<DetailpersonilEvent, DetailpersonilState>
DetailpersonilBloc() : super(const DetailpersonilInitial())
on<Detail>((event, emit) async
SharedPreferences pref = await SharedPreferences.getInstance();
String name = pref.getString('nama');
String nrp = pref.getString('NRP');
String pangkat = pref.getString('pangkat');
String jabatan = pref.getString('jabatan');
String satker = pref.getString('satker');
String polda = pref.getString('polda');
String npwp = pref.getString('NPWP');
String rekening = pref.getString('rekening');
String bank = pref.getString('bank');
emit(Loaded(
name,
nrp,
pangkat,
jabatan,
satker,
polda,
npwp,
rekening,
bank,
));
);
DetailpersonilEvent
part of 'detailpersonil_bloc.dart';
@immutable
abstract class DetailpersonilEvent extends Equatable
class Detail extends DetailpersonilEvent
@override
List<Object> get props => [];
详细个人状态
part of 'detailpersonil_bloc.dart';
@immutable
abstract class DetailpersonilState extends Equatable
final String nama;
final String nrp;
final String pangkat;
final String jabatan;
final String satker;
final String polda;
final String npwp;
final String rekening;
final String bank;
const DetailpersonilState(
this.nama,
this.nrp,
this.pangkat,
this.jabatan,
this.satker,
this.polda,
this.npwp,
this.rekening,
this.bank);
class DetailpersonilInitial extends DetailpersonilState
const DetailpersonilInitial()
: super(
nama: 'Nama',
nrp: 'NRP',
pangkat: 'Pangkat',
jabatan: 'Jabatan',
satker: 'Satker',
polda: 'Polda',
npwp: 'NPWP',
rekening: 'No Rekening',
bank: 'Nama Bank',
);
@override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
class Loaded extends DetailpersonilState
const Loaded(
String nama,
String nrp,
String pangkat,
String jabatan,
String satker,
String polda,
String npwp,
String rekening,
String bank,
) : super(
nama: nama,
nrp: nrp,
pangkat: pangkat,
jabatan: jabatan,
satker: satker,
polda: polda,
npwp: npwp,
rekening: rekening,
bank: bank);
@override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
登录页面
class LoginPage extends StatefulWidget
const LoginPage(Key key) : super(key: key);
@override
_LoginPageState createState() => _LoginPageState();
class _LoginPageState extends State<LoginPage>
double width = 0;
double height = 0;
TextEditingController nrpController = TextEditingController();
TextEditingController sandiController = TextEditingController();
final formKey = GlobalKey<FormState>();
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
TunkinBloc tunkinBloc;
bool passwordVisible = false;
@override
void initState()
super.initState();
checkToken();
void checkToken() async
SharedPreferences pref = await SharedPreferences.getInstance();
if (pref.getString('token') != null)
detailPersonilBloc = DetailpersonilBloc();
gajiBloc = GajiBloc();
tunkinBloc = TunkinBloc();
detailPersonilBloc.add(Detail());
gajiBloc.add(Gaji());
tunkinBloc.add(Tunkin());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
onLogin(DetailpersonilBloc detailPersonilBloc) async
if (formKey.currentState.validate())
var token = await APIService.generateToken(
nrpController.text, sandiController.text);
if (token != null)
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.setString('token', token.token);
var detail = await APIService.getDetailPersonil(token.token);
await pref.setString('nama', detail.nMPEG);
await pref.setString('NRP', detail.nRP);
await pref.setString('pangkat', detail.nMGOL1);
await pref.setString('jabatan', detail.sEBUTJAB);
await pref.setString('satker', detail.nMSATKER);
await pref.setString('polda', detail.nMUAPPAW);
await pref.setString('NPWP', detail.nPWP);
await pref.setString('rekening', detail.rEKENING);
await pref.setString('bank', detail.nMBANK);
nrpController.clear();
sandiController.clear();
detailPersonilBloc.add(Detail());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
else
showDialog(
context: context,
builder: (context)
return AlertDialog(
title: Text('Error'),
content: Text('Login Gagal'),
actions: [
ElevatedButton(
onPressed: ()
Navigator.of(context).pop();
,
child: Text('Tutup'),
),
],
);
,
);
@override
Widget build(BuildContext context)
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Opacity(
opacity: 0.5,
child: Image(
image: AssetImage('images/bg-map-min.png'),
),
),
],
),
SingleChildScrollView(
padding: EdgeInsets.only(top: 100),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 100,
width: width,
child: const Image(
image: AssetImage('images/login-logo.png'),
alignment: Alignment.center,
),
),
Container(
padding: const EdgeInsets.all(15),
child: const Text(
'GAJI DAN TUNKIN',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
Form(
key: formKey,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(20 - 2.6),
child: Card(
elevation: 10,
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(bottom: 20),
child: const Text(
'LOGIN',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
validator: (value)
if (value == null || value.isEmpty)
return 'Masukkan NRP/NIP';
return null;
,
controller: nrpController,
decoration: InputDecoration(
labelText: 'NRP/NIP',
hintText: 'Masukkan NRP/NIP',
prefixIcon: Icon(Icons.person,
color: Colors.blue.shade700),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
TextFormField(
obscureText: !passwordVisible,
validator: (value)
if (value == null || value.isEmpty)
return 'Masukkan Kata Sandi';
return null;
,
controller: sandiController,
decoration: InputDecoration(
labelText: 'Kata Sandi',
hintText: 'Masukkan Kata Sandi',
prefixIcon: Icon(Icons.lock,
color: Colors.blue.shade700),
suffixIcon: IconButton(
onPressed: ()
setState(()
passwordVisible = !passwordVisible;
);
,
icon: Icon(
passwordVisible
? Icons.visibility
: Icons.visibility_off,
color: Colors.blue.shade700,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
)
],
),
),
),
),
ElevatedButton(
onPressed: () async
await onLogin(detailPersonilBloc);
,
child: const Text('LOGIN'),
style: ElevatedButton.styleFrom(
primary: Colors.blue.shade700,
minimumSize: const Size(200, 40),
),
)
],
),
),
],
),
),
],
),
);
GajiPage
class GajiPage extends StatefulWidget
const GajiPage(Key key) : super(key: key);
@override
_GajiPageState createState() => _GajiPageState();
class _GajiPageState extends State<GajiPage>
double width = 0;
double height = 0;
var currentYear = DateTime.now().year;
var currentMonth = DateTime.now().month;
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
@override
void initState()
setState(()
detailPersonilBloc = DetailpersonilBloc();
detailPersonilBloc.add(Detail());
setMonth();
setYear();
gajiBloc = GajiBloc();
gajiBloc.add(Gaji());
);
super.initState();
@override
Widget build(BuildContext context)
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Image(
image: const AssetImage('images/header-logo.png'),
width: width / 2,
),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 170, 177, 175),
Color.fromARGB(255, 197, 217, 212)
],
),
),
),
),
body: Stack(
children: [
BlocBuilder<GajiBloc, GajiState>(
builder: (context, state)
return state is GajiLoaded
? ListView(
children: [
Container(
height: 100,
),
Card(
color: const Color.fromARGB(255, 74, 50, 152),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.all(10),
child: const Text(
'Gaji Bersih',
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
Container(
margin: const EdgeInsets.all(10),
child: Text(
NumberFormat.currency(
locale: 'en',
symbol: 'RP ',
decimalDigits: 0)
.format(state.bersih),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 40,
color: Colors.white,
),
),
),
],
),
),
Card(
child: Column(
children: [
Container(
color: const Color.fromARGB(255, 238, 238, 238),
width: width,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Container(
margin: const EdgeInsets.fromLTRB(
10, 10, 0, 10),
width: (width / 2) - 25,
child: const Text(
'Detail Gaji',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
),
Container(
margin: const EdgeInsets.fromLTRB(
5, 10, 20, 10),
width: (width / 2) - 18,
child: Text(
'$state.bulan - $state.tahun',
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
)
],
),
),
...
],
),
),
Container(
height: 50,
),
],
)
: Center(
child: Text(
'Tidak ada data. Data gaji bulan $state.bulan belum diproses'),
);
,
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: BlocBuilder<DetailpersonilBloc, DetailpersonilState>(
builder: (context, state)
return Card(
child: Row(
children: [
Container(
margin: const EdgeInsets.all(10),
child: const CircleAvatar(
backgroundImage: AssetImage('images/Profpic.PNG'),
radius: 30,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 250,
padding: const EdgeInsets.all(5),
child: Text(
state.nama,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
)),
Container(
padding: const EdgeInsets.all(5),
child: Text(
state.nrp,
style: const TextStyle(fontSize: 15),
)),
],
),
GestureDetector(
onTap: ()
detailPersonilBloc.add(Detail());
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => detailsBottomSheet(),
);
,
child: const Text(
'DETAILS',
style: TextStyle(color: Colors.blue),
),
)
],
),
);
,
),
),
GestureDetector(
onTap: ()
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context)
return StatefulBuilder(
builder: (context, StateSetter setState)
return filterBottomSheet();
,
);
,
);
,
child: Container(
height: 50,
width: width,
decoration: const BoxDecoration(
color: Color.fromARGB(255, 244, 244, 244),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 3),
child: const Icon(
Icons.tune,
color: Color.fromARGB(255, 45, 165, 217),
),
),
const Text(
'Filter',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
color: Color.fromARGB(255, 45, 165, 217),
),
),
],
),
),
)
],
)
],
),
);
注2:“...”是复制不需要的一堆代码。
【问题讨论】:
【参考方案1】:答案非常简单,正如我从实习中学到的一样。 Bloc 没有更新的原因是因为我没有使用应用程序的上下文。所以当我在我的页面中生成一个新的 Bloc 时,它是“空的”。所以解决方案是使用 context.read().add(BlocEvent()) 或使用 BlocProvider.of(context) 创建一个新块,然后添加事件。基本上必须为 bloc 提供应用程序的原始上下文。
【讨论】:
以上是关于为啥尽管观察者的状态发生变化,但应用程序重启后 Flutter BloC UI 没有更新?的主要内容,如果未能解决你的问题,请参考以下文章