brianegan / brianegan/flutter_redux
BloC store Model to Redux without being noticed if I call BloC event in Redux onInit
- Dominant language
- Dart
- Stars
- 1.6k
- Forks
- 207
- PR merge metrics
- No merged PRs in 30d
Description
I use Redux to handle my globalstate, and BloC as localstate. I need to store my globalmodel in Redux to my BloC model on Redux onInit. I call BloC event in Redux onInit to get my BloC model same as globalmodel. But, I got my Redux model change even if I dont callback Redux update model methode when my BloC doing update model. But if I didn't call BloC event in Redux onInit, the model keep flow well. How I can handle this, so that my globalmodel didn't change when I update my BloC model?
this is my code:
import 'dart:async';
import 'package:kmb/redux/actions.dart';
import 'package:kmb/redux/global-model.dart';
import 'package:kmb/src/models/model-account.dart';
import 'package:kmb/src/views/settings-page/place-page/controller/place-bloc.dart';
import 'package:kmb/src/views/settings-page/place-page/signup-map-dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
class PlaceParent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context)=>PlaceBloc(),
child: PlacePage(),
);
}
}
class PlacePage extends StatefulWidget {
@override
_PlacePageState createState() => _PlacePageState();
}
class _PlacePageState extends State {
TextEditingController addressCntrl = new TextEditingController();
Completer _controller = Completer();
final GlobalKey _scaffoldKey = new GlobalKey();
GlobalModel informasi;
PlaceBloc provider;
Set _marker = {};
GoogleMapController mapController;
@override
void initState() {
super.initState();
informasi = GlobalModel.initState();
provider = BlocProvider.of(context);
}
@override
void dispose() {
provider.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return StoreConnector(
key: Key('StoreLocation'),
converter: (store)=>(informasiGlobal)=>(){
store.dispatch(ActionStoringInformation(informasi));
},
onInit: (stored){
print(stored.state.accountInfo.toMap());
informasi = stored.state;
provider.add(PlaceBlocEventStoreInit(informasi.accountInfo));
addressCntrl.text = stored.state.accountInfo.address;
},
onWillChange: (prevstate, nextstate){
print('prevs: $prevstate');
print('prevs: $nextstate');
},
builder: (context, callback){
return BlocListener(
bloc: BlocProvider.of(context),
listener: (context, state){
if(!state.updateOnProccess){
if(state.updateOnFinish){
print('Masuk Sini CUK');
informasi.accountInfo.updatePoint(state.account);
callback(informasi);
Navigator.of(context).pop();
provider.add(PlaceBlocEventNormalize());
}
}
if(!state.updateLocOnProccess){
if(state.updateLocOnFinish){
addressCntrl.text = state.account.address;
mapController.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(
target: LatLng(state.account.pointLat, state.account.pointLon),
zoom: 16.0
)));
_marker.clear();
_marker.add(Marker(
markerId: MarkerId('001'),
position: LatLng(state.account.pointLat, state.account.pointLon)
));
provider.add(PlaceBlocEventNormalize());
}
}
},
child: BlocBuilder(
builder: (context, state){
return Scaffold(
key: _scaffoldKey,
body: Container(
decoration: BoxDecoration(
color: Colors.white
),
child: Padding(
padding: const EdgeInsets.only(left:20.0, right:20.0),
child: ListView(
children: [
Divider(
color: Colors.transparent,
height: 30.0,
),
Row(
children: [
InkWell(
child: Icon(Icons.close),
onTap: (){
Navigator.of(context).pop();
},
),
VerticalDivider(
color: Colors.transparent,
width: 10.0,
),
Text('place Point', style: TextStyle(fontSize: 24.0),)
],
),
Divider(
color: Colors.transparent,
height: 20.0,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5.0),
bottomRight: Radius.circular(5.0),
),
),
child: Column(
children: [
Container(
height: 200.0,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(-6.1753924, 106.8249641),
zoom: 16.0
),
onMapCreated: (controller){
mapController = controller;
// if(state.account.pointLat!=null && state.account.pointLon!=null){
// mapController.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(
// target: LatLng(state.account.pointLat, state.account.pointLon),
// zoom: 16.0
// )));
// _marker.add(Marker(
// markerId: MarkerId('001'),
// position: LatLng(state.account.pointLat, state.account.pointLon)
// ));
// }
_controller.complete(controller);
},
markers: _marker,
),
),
Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5.0),
bottomRight: Radius.circular(5.0),
),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0,10.0,16.0,10.0),
child: Container(
child: InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.launch, color: Colors.white, size: 32.0,),
VerticalDivider(
color: Colors.transparent,
width: 10.0,
),
Text('Open Map', style: TextStyle(color: Colors.white, fontSize: 16.0, fontWeight: FontWeight.w600),)
],
),
onTap: (){
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
print(state.account.pointLon);
return state.account.pointLon!=null? SignUpMapDialog(
currPositionLat: state.account.pointLat,
currPositionLon: state.account.pointLon,
onItemChoosed: (locparam){
provider.add(PlaceBlocEventStoreUpdateOnProcess());
provider.add(PlaceBlocEventStoreUpdate(Account(pointLat: locparam.pointLoc.latitude, pointLon: locparam.pointLoc.longitude, address: locparam.address)));
},
):SignUpMapDialog(
currPositionLat: -6.1753924,
currPositionLon: 106.8249641,
onItemChoosed: (locparam){
provider.add(PlaceBlocEventStoreUpdateOnProcess());
provider.add(PlaceBlocEventStoreUpdate(Account(pointLat: locparam.pointLoc.latitude, pointLon: locparam.pointLoc.longitude, address: locparam.address)));
},
);
}
);
},
),
),
),
)
],
),
),
Divider(
color: Colors.transparent,
height: 30.0,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Latitude', style: TextStyle(fontWeight: FontWeight.w600,fontSize: 16.0),),
Divider(height: 4.0, color: Colors.transparent,),
Text('${state.account.pointLat}', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
],
),
Padding(
padding: const EdgeInsets.only(top:10.0, bottom:10.0),
child: Divider(height: 1.0, color: Colors.grey,),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Longitude', style: TextStyle(fontWeight: FontWeight.w600,fontSize: 16.0),),
Divider(height: 4.0, color: Colors.transparent,),
Text('${state.account.pointLon}', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
],
),
Padding(
padding: const EdgeInsets.only(top:10.0, bottom:10.0),
child: Divider(height: 1.0, color: Colors.grey,),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Address', style: TextStyle(fontWeight: FontWeight.w600,fontSize: 16.0),),
Container(
child: TextFormField(
decoration: InputDecoration(
hintText: "Address",
),
controller: addressCntrl,
),
),
],
),
Divider(
color: Colors.transparent,
height: 30.0,
),
InkWell(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.all(Radius.circular(5.0))
),
child: Padding(
padding: const EdgeInsets.fromLTRB(10.0,14.0,10.0,14.0),
child: Center(child: Text('Confirm', style: TextStyle(fontSize: 18.0, color: Colors.white, fontWeight: FontWeight.w700),)),
)
),
onTap: (){
provider.add(PlaceBlocEventUpdateRemoteOnProcess());
provider.add(PlaceBlocEventUpdateRemote(state.account));
},
),
Divider(
color: Colors.transparent,
height: 30.0,
),
],
),
),
),
);
}
)
);
}
);
}
}
typedef OnStoringInformationUpdatePointCallback = Function(GlobalModel informasiGlobal);
my Bloc code:
import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:kmb/src/models/model-account.dart';
import 'package:kmb/src/services/api-serv.dart';
import 'package:equatable/equatable.dart';
import 'package:kmb/src/globals.dart' as globals;
import 'package:http/http.dart';
class PlaceBlocModel{
Account account;
bool onInit;
bool updateOnProccess;
bool updateOnFinish;
bool updateLocOnProccess;
bool updateLocOnFinish;
PlaceBlocModel({
this.account,
this.onInit,
this.updateOnProccess,
this.updateOnFinish,
this.updateLocOnFinish,
this.updateLocOnProccess
});
PlaceBlocModel.initState(){
this.account = Account.initState();
this.onInit = true;
this.updateOnProccess = false;
this.updateOnFinish = false;
this.updateLocOnProccess = false;
this.updateLocOnFinish = false;
}
void fromMapUpdate(Map map){
this.account.pointLat = map['data']['point_lat'];
this.account.pointLon = map['data']['point_lon'];
this.account.address = map['data']['address'];
this.account.token = map['token'];
}
void locUpdate(Account accountMap){
this.account.pointLat = accountMap.pointLat;
this.account.pointLon = accountMap.pointLon;
this.account.address = accountMap.address;
}
PlaceBlocModel.copy(PlaceBlocModel state){
this.account = state.account;
this.onInit = state.onInit;
this.updateOnProccess = state.updateOnProccess;
this.updateOnFinish = state.updateOnFinish;
this.updateLocOnProccess = state.updateLocOnProccess;
this.updateLocOnFinish = state.updateLocOnFinish;
}
}
abstract class PlaceBlocEvent extends Equatable{
@override
List get props => [];
}
class PlaceBlocEventStoreInit extends PlaceBlocEvent{
final Account account;
PlaceBlocEventStoreInit(this.account);
}
class PlaceBlocEventStoreUpdate extends PlaceBlocEvent{
final Account account;
PlaceBlocEventStoreUpdate(this.account);
}
class PlaceBlocEventStoreUpdateOnProcess extends PlaceBlocEvent{}
class PlaceBlocEventUpdateRemoteOnProcess extends PlaceBlocEvent{}
class PlaceBlocEventUpdateRemote extends PlaceBlocEvent{
final Account account;
PlaceBlocEventUpdateRemote(this.account);
}
class PlaceBlocEventNormalize extends PlaceBlocEvent{}
class PlaceBloc extends Bloc{
@override
PlaceBlocModel get initialState => PlaceBlocModel.initState();
@override
Stream mapEventToState(PlaceBlocEvent event)async* {
if(event is PlaceBlocEventUpdateRemote){
print(event.account.toMapPlacePoint());
PlaceBlocModel retval = await PlacePointUpdate(event.account.toMapPlacePoint(), state);
yield retval;
}else if(event is PlaceBlocEventStoreInit){
if(state.onInit){
yield PlaceBlocModel(account: event.account, onInit: false, updateOnFinish: false, updateOnProccess: false, updateLocOnFinish: false, updateLocOnProccess: false);
}
}else if(event is PlaceBlocEventUpdateRemoteOnProcess){
yield PlaceBlocModel(account: state.account, onInit: false, updateOnFinish: false, updateOnProccess: true, updateLocOnFinish: state.updateLocOnFinish, updateLocOnProccess: state.updateLocOnProccess);
}else if(event is PlaceBlocEventStoreUpdateOnProcess){
yield PlaceBlocModel(account: state.account, onInit: false, updateOnFinish: state.updateOnFinish, updateOnProccess: state.updateOnProccess, updateLocOnFinish: false, updateLocOnProccess: true);
}else if(event is PlaceBlocEventStoreUpdate){
PlaceBlocModel currState = PlaceBlocModel.copy(state);
currState.locUpdate(event.account);
currState.updateLocOnFinish = true;
currState.updateLocOnProccess = false;
yield currState;
}else if(event is PlaceBlocEventNormalize){
yield PlaceBlocModel(account: state.account, onInit: false, updateOnFinish: false, updateOnProccess: false, updateLocOnFinish: false, updateLocOnProccess: false);
}
}
Future PlacePointUpdate(Map map, PlaceBlocModel state)async{
final Response response = await callApi(APIMethode.POST, globals.urlUpdatePlacePoint(), data: map);
if(response.statusCode == 200){
final jsonRes = json.decode(response.body);
if(jsonRes['error']==true){
return PlaceBlocModel(account: state.account, updateOnFinish: true, updateOnProccess: false);
}else{
print(map);
PlaceBlocModel Place = PlaceBlocModel.copy(state)..fromMapUpdate(jsonRes);
Place.updateOnFinish = true;
Place.updateOnProccess = false;
return Place;
}
}else{
return PlaceBlocModel(account: state.account, updateOnFinish: true, updateOnProccess: false);
}
}
}
Contributor guide
No contributing guide indexed for this repository
Research direction
Start at the StoreConnector onInit and converter in the provided PlacePage code, then inspect how GlobalModel and Account are copied or mutated during PlaceBlocEventStoreInit. Reproduce the Redux and BloC update sequence and determine whether the reported behavior is expected or should be covered by a focused regression test; the issue does not name a repository file or existing test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart, flutter
- Domain
- frontend, mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100