Closed LeandroMoura3 closed 4 years ago
Você colocou nearClub no equals do ViewModel, mas se a sua action falhar ele vai mudar somente o gpsStatus. Por isso, imagino que o seu equals deveria conter o gpsStatus também.
Fora isso tudo parece certo. Pode ser um erro no copyWith do state ou do user. Ou pode ser um problema no equals/hashcode no nearClub. Ou pode ser outra coisa.
Não faz sentido esse await de 2 segundos depois do seu dispatch, pois daí o dispatch já terminou. O que importa se vc espera dois segundos depois? Talvez o que você marcou como "...outros afazeres" é que tenha alguma influência, não sei.
Para eu dar mais informação você precisa criar pra mim um código minimo que demonstre o problema. Por favor, crie um arquivo único, com um método main que eu possa rodar e ver o problema por mim mesmo. Sugiro você usar http://numbersapi.com em vez do GPS, para facilitar a criação desse arquivo de demonstração.
Dentro dos meus conhecimentos, verifiquei os hashcodes/equals, assim como os copyWiths, tbm coloquei o gpsStatus no equals, ainda continua o mesmo erro. Sem o delay não atualiza, com o delay atualiza. Realmente estranhíssimo este comportamento. Vou trabalhar em breve num código de arquivo único para tentar reproduzir este erro (acho que amanhã, no máximo, devo começar).
Agradeço.
Boa noite,
Consegui isolar o erro:
import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart';
//Store
class Club {
final String name;
const Club({
this.name,
});
Club copyWith({
String name,
}) =>
Club(
name: name ?? this.name,
);
@override
String toString() {
return 'Club{name: $name}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Club && runtimeType == other.runtimeType && name == other.name;
@override
int get hashCode => name.hashCode;
}
class MyAppState {
final List<Club> clubList;
const MyAppState({
this.clubList,
});
MyAppState copyWith({
List<Club> clubList,
}) =>
MyAppState(
clubList: clubList ?? this.clubList,
);
static MyAppState initialState() => MyAppState(
clubList: [],
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MyAppState &&
runtimeType == other.runtimeType &&
clubList == other.clubList;
@override
int get hashCode => clubList.hashCode;
@override
String toString() {
return 'MyAppState{clubList: $clubList}';
}
}
Store<MyAppState> store = Store<MyAppState>(initialState: MyAppState.initialState());
//Actions
class UpdateClubListAction extends ReduxAction<MyAppState> {
@override
MyAppState reduce() {
List<Club> currentClubList = List.generate(
15,
(index) => Club(name: "name$index"),
).toList();
return state.copyWith(
clubList: currentClubList,
);
}
@override
void after() => print("db1:" + state.clubList.length.toString());
}
class PrintClubListAction extends ReduxAction<MyAppState> {
@override
MyAppState reduce() {
print("db2:" + state.clubList.length.toString());
return state.copyWith();
}
}
//Viewmodel
class HomeSync extends BaseModel<MyAppState> {
HomeSync();
void Function(BuildContext) pushSelectClub;
int clubListLength;
HomeSync.build({
@required this.clubListLength,
@required this.pushSelectClub,
}) : super(equals: [clubListLength]);
@override
HomeSync fromStore() => HomeSync.build(
clubListLength: state.clubList.length,
pushSelectClub: (BuildContext context) async {
dispatch(UpdateClubListAction());
dispatch(PrintClubListAction());
print("db3:" + state.clubList.length.toString());
print("db4:" + clubListLength.toString());
},
);
}
//app
void main() => runApp(MyApp(store));
class MyApp extends StatefulWidget {
final store;
MyApp(this.store);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return StoreProvider<MyAppState>(
store: widget.store,
child: MaterialApp(
title: 'Async Redux Management State',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeTab(),
),
);
}
}
class HomeTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector<MyAppState, HomeSync>(
model: HomeSync(),
builder: (BuildContext context, HomeSync sync) {
return Scaffold(
appBar: AppBar(
title: Text("TestWidget"),
),
body: Center(
child: RaisedButton(
onPressed: () => sync.pushSelectClub(context),
child: Text("Click me"),
),
),
);
},
);
}
}
O recebido é: I/flutter (18834): db1:15 I/flutter (18834): db2:15 I/flutter (18834): db3:0 I/flutter (18834): db4:null
O esperado seria: I/flutter (18834): db1:15 I/flutter (18834): db2:15 I/flutter (18834): db3:15 I/flutter (18834): db4:15
Esse ainda não é o caso em que, se esperar, funciona, mas acredito que tenha uma forte relação.
Olá,
Consegui isolar a achar a inconsistência que comentei, que tratava-se sobre a necessidade de inserir um delay pra surtir o efeito desejado. Percebi que não tinha haver com o tipo de chamada ser assíncrona ou não. Segue o código demonstrativo:
import 'package:async_redux/async_redux.dart';
import 'package:flutter/material.dart';
//Store
class Club {
final String name;
const Club({
this.name,
});
Club copyWith({
String name,
}) =>
Club(
name: name ?? this.name,
);
@override
String toString() {
return 'Club{name: $name}';
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Club && runtimeType == other.runtimeType && name == other.name;
@override
int get hashCode => name.hashCode;
}
class MyAppState {
final List<Club> clubList;
final bool statusBool;
const MyAppState({
this.clubList,
this.statusBool,
});
MyAppState copyWith({
List<Club> clubList,
bool statusBool,
}) =>
MyAppState(
clubList: clubList ?? this.clubList,
statusBool: statusBool ?? this.statusBool,
);
static MyAppState initialState() => MyAppState(
clubList: [],
statusBool: false,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MyAppState &&
runtimeType == other.runtimeType &&
clubList == other.clubList &&
statusBool == other.statusBool;
@override
int get hashCode => clubList.hashCode ^ statusBool.hashCode;
@override
String toString() {
return 'MyAppState{clubList: $clubList, statusBool: $statusBool}';
}
}
Store<MyAppState> store = Store<MyAppState>(initialState: MyAppState.initialState());
//Actions
class UpdateClubListAction extends ReduxAction<MyAppState> {
@override
MyAppState reduce() {
List<Club> currentClubList = List.generate(
15,
(index) => Club(name: "name$index"),
).toList();
return state.copyWith(
clubList: currentClubList,
);
}
@override
void after() => print("db1:" + state.clubList.length.toString());
}
class PrintClubListAction extends ReduxAction<MyAppState> {
@override
MyAppState reduce() {
print("db2:" + state.clubList.length.toString());
return state.copyWith();
}
}
class ChangeStatusBoolAction extends ReduxAction<MyAppState> {
@override
MyAppState reduce() {
return state.copyWith(
statusBool: !state.statusBool,
);
}
@override
void after() {
print("db5:"+state.statusBool.toString());
super.after();
}
}
//Viewmodel
class HomeSync extends BaseModel<MyAppState> {
HomeSync();
void Function(BuildContext) pushSelectClub;
VoidCallback changeStatusBool;
int clubListLength;
bool statusBool;
HomeSync.build({
@required this.clubListLength,
@required this.pushSelectClub,
@required this.statusBool,
@required this.changeStatusBool,
}) : super(equals: [clubListLength, statusBool]);
@override
HomeSync fromStore() => HomeSync.build(
statusBool: state.statusBool,
clubListLength: state.clubList.length,
changeStatusBool: (){
dispatch(ChangeStatusBoolAction());
print("db6:"+statusBool.toString());
print("db7:"+state.statusBool.toString());
},
pushSelectClub: (BuildContext context) async {
dispatch(UpdateClubListAction());
await Future.delayed(Duration(seconds: 2));
dispatch(PrintClubListAction());
print("db3:" + state.clubList.length.toString());
print("db4:" + clubListLength.toString());
},
);
}
//app
void main() => runApp(MyApp(store));
class MyApp extends StatefulWidget {
final store;
MyApp(this.store);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return StoreProvider<MyAppState>(
store: widget.store,
child: MaterialApp(
title: 'Async Redux Management State',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeTab(),
),
);
}
}
class HomeTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StoreConnector<MyAppState, HomeSync>(
model: HomeSync(),
builder: (BuildContext context, HomeSync sync) {
return Scaffold(
appBar: AppBar(
title: Text("TestWidget"),
),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
onPressed: sync.changeStatusBool,
child: Text("Click me first"),
),
RaisedButton(
onPressed: () => sync.pushSelectClub(context),
child: Text("Click me second"),
),
Text(sync.statusBool.toString()),
Text(sync.clubListLength.toString()),
],
),
),
);
},
);
}
}
Aqui este código se comporta da seguinte maneira: Buildei o app, apertei no primeiro botão, em seguida no segundo, segue o resultado: I/flutter (21302): db5:true I/flutter (21302): db6:null I/flutter (21302): db7:false I/flutter (21302): db1:15 I/flutter (21302): db2:15 I/flutter (21302): db3:15 I/flutter (21302): db4:null
Dou hot-restart. Aperto somente no segundo botão, recebo o seguinte resultado: I/flutter (21302): db1:15 I/flutter (21302): db2:15 I/flutter (21302): db3:0 I/flutter (21302): db4:null
Comentei o delay, salvei e dei hot-restart, apertei no primeiro botão, depois no segundo, recebo o seguinte resultado: I/flutter (21302): db5:true I/flutter (21302): db6:null I/flutter (21302): db7:false I/flutter (21302): db1:15 I/flutter (21302): db2:15 I/flutter (21302): db3:0 I/flutter (21302): db4:null
Ainda com o delay comentado, dei hot-restart, apertei no segunte botão, recebi o seguinte resultado: I/flutter (21302): db1:15 I/flutter (21302): db2:15 I/flutter (21302): db3:0 I/flutter (21302): db4:null
Tirei o async da assinatura da função anônima do pushSelectClub e deu o mesmo resultado.
Como é possível perceber, a presença do delay alterou os resultados. Novamente, o que eu esperava nestes resultados é que fosse 15 em todas as consultas de tamanho.
No entanto, eu percebi também que o resultado do viewmodel na view, ou seja, o Text(sync.clubListLength.toString()), está funcionando perfeitamente, o que me faz perguntar se sou eu que estou fazendo uma enorme confusão. Se for o caso, desculpe-me, mas, de qualquer forma, ainda acho inconsistente essa questão de ter ou não ter o delay, pois em teoria ele não deveria surtir efeito.
Se for do resultado esperado for o que está ai, ou seja, 15, 15, 0 e null lá no primeiro teste do comentário anterior, existe uma forma de eu forçar a atualização do store na viewmodel? Pois eu gerencio nas viewmodels tanto as views (através de globalkeys) como a store(através das actions) e apis, e acessar as views por meio de actions não seria tão bom pro padrão de projeto que estou estabelecendo.
Como vc mesmo disse está tudo funcionando corretamente, você implementou correto. Você só está fazendo uma confusão em relação a como você acha que isso tudo deveria funcionar. Que tal eu explicar tudo pra você por telefone? Me passa seu whatsapp por comentário aqui (e depois você dá um delete no comentário), ou então me manda por email, se vc quiser, e eu te ligo, que tal?
Estou com o mesmo problema atualmente num bloco de catch da minha aplicacao, como foi solucionado esse problema?
Obrigado!
Leonardo, é dificil saber o que vc quer dizer com problema num bloco de catch. Melhor passar um código aqui. De preferência o menor código possível que demonstre o problema.
Olá,
Eu estive enfrentando o seguinte problema: eu chamo uma ação assíncrona, esperando sua finalização, em um dos viewmodels. O esperado seria que, assim que ela terminasse, a store do viewmodel fosse atualizada. Porém, para que isto aconteça, eu tive que colocar uma espera de alguns segundos. Estou achando este comportamento estranho. Em códigos, está acontecendo o seguinte:
Se eu ponho esta espera de dois segundos, funciona como esperado. Se não, é como se a store desta viewmodel (NavBarSync) não fosse atualizada.
Aqui está a action:
Estou escrevendo a issue em português, posso passá-la para o inglês (embora ele seja péssimo, hehehe) caso tenha entendido algo errado.