Flutter BlocConsumer doesn't listen to state change when searching - flutter

I have been battling with this flutter bloc problem. I am currently using flutter Bloc 7.0.1. The BlocConsumer doesn't listen to the state changes at all. Anytime I enter values inside the search field, event is been called and state is yielded but the listener fail to listen to state changes.
This issue is really driving me mad.
STATE
part of 'people_bloc.dart';
#immutable
abstract class PeopleState {}
class PeopleInitial extends PeopleState {}
class PeopleLoadingState extends PeopleState {
#override
List<Object?> get props => [];
}
class SearchLoadingState extends PeopleState {
#override
List<Object?> get props => [];
}
BLOC
List<SearchPeopleResponseData> people = [];
#override
Stream<PeopleState> mapEventToState(
PeopleEvent event,
) async* {
if (event is SearchPeopleEvent) {
yield SearchLoadingState();
try {
var token = await getToken();
//print(token);
SearchPeopleResponse responseData =
await client.getPeople(token!, event.term);
if (responseData.status == 200) {
yield GetSearchResultState(getPeopleResponse: responseData);
} else {
yield PeopleErrorState(message: responseData.msg);
print("loadingE");
}
} catch (e) {
//print("error msg here ${e.toString()}");
PeopleErrorState(message: e.toString());
}
}
EVENT
part of 'people_bloc.dart';
#immutable
abstract class PeopleEvent {
const PeopleEvent();
}
class GetPeopleEvent extends PeopleEvent {
final String term;
GetPeopleEvent({required this.term});
#override
List<Object> get props => [term];
}
class SearchPeopleEvent extends PeopleEvent {
final String term;
SearchPeopleEvent({required this.term});
#override
List<Object> get props => [term];
}
VIEW
Widget build(BuildContext context) {
return BlocConsumer<PeopleBloc, PeopleState>(
listener: (context, state) {
print("Listener has been called");
if (state is GetSearchResultState) {
loading = false;
print("Result Found in view");
} else if (state is SearchLoadingState) {
loading = true;
print("Search loading");
} else if (state is PeopleLoadingState) {
loading = true;
}
See screenshot

Related

Flutter bloc rebuild on change state

For filter my list I use a state FilterState.
I have in this state my list filter but my widget for build list is not rebuild.
My print shows that the list is correct according to the state.
But GameList keeps its initial state which is not filtered : it's not rebuild.
Thanks,
my page for state :
BlocBuilder<GameBloc, GameState>(
builder: (context, state) {
if (state is LoadingState) {
return buildLoading();
} else if (state is FailState) {
return ErrorUI(message: state.message);
} else if (state is ListLoadedState) {
_list = state.list;
return GameList(list: state.list!);
} else if (state is FilterGamesState) {
print(state.list);
return GameList(list: state.list!);
}
return Container();
},
),
Bloc Page :
class GameBloc extends Bloc<GameEvent, GameState> {
GameBloc({required this.repository}) : super(LoadingState()) {
on<BackEvent>(_onBackEvent);
on<FetchGamesEvent>(_onFetchList);
on<FetchGameEvent>(_onFetchItem);
on<SavingEvent>(_onSavingEvent);
on<UpdateGameEvent>(_onUpdate);
on<CreateGameEvent>(_onCreate);
on<FilterGamesEvent>(_onFilter);
}
GameRepository repository;
final currentFilter = BehaviorSubject<Map<String, dynamic>>();
Future<void> _onFilter(
FilterGamesEvent event,
Emitter<GameState> emit,
) async {
try {
emit(LoadingState());
final list = event.list?.where((Game item) {
if (currentFilter.value.containsKey('player')) {
int players = currentFilter.value['player'].nb;
return players.isBetween(from: item.nopMin, to: item.nopMax);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('age')) {
return item.age!.isBetween(
from: currentFilter.value['age'].min,
to: currentFilter.value['age'].max);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('duration')) {
return compareToDuration(
item.durMin!,
item.durMax!,
currentFilter.value['duration'].min,
currentFilter.value['duration'].max);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('tags')) {
return item.tags!
.compareToList(currentFilter.value['tags'] as List<String>);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('collection')) {
return item.collection!
.compareToList(currentFilter.value['collection'] as List<String>);
}
return true;
}).toList();
emit(FilterGamesState(listGame: list!));
} catch (e) {
emit(const FailState(message: 'Failed to fetch all games data.'));
}
}
Event Page :
abstract class GameEvent extends Equatable {
final Game? item;
const GameEvent({this.item});
#override
List<Object> get props => [];
}
class InitialEvent extends GameEvent {
const InitialEvent({required Game item}) : super(item: item);
}
class BackEvent extends GameEvent {}
class SavingEvent extends GameEvent {}
class FetchGameEvent extends GameEvent {
const FetchGameEvent({required Game item}) : super(item: item);
}
class FetchGamesEvent extends GameEvent {}
class FilterGamesEvent extends GameEvent {
const FilterGamesEvent({required this.list});
final List<Game>? list;
}
State Page :
abstract class GameState extends Equatable {
final Game? item;
final List<Game>? list;
const GameState({this.item, this.list});
#override
List<Object> get props => [];
}
class GameInitial extends GameState {}
class FailState extends GameState {
const FailState({required this.message});
final String message;
}
class LoadingState extends GameState {}
class ListLoadedState extends GameState {
const ListLoadedState({required this.listGame}) : super(list: listGame);
final List<Game> listGame;
}
class ItemLoadedState extends GameState {
const ItemLoadedState({required this.game}) : super(item: game);
final Game game;
}
class FilterGamesState extends GameState {
const FilterGamesState({required this.listGame}) : super(list: listGame);
final List<Game> listGame;
}
I resolved this,
I send the key to GameList in FilterGameState.
else if (state is FilterGamesState) {
print(state.list);
return GameList(key: GlobalKey<GameFilterState>(), list: state.list!);
}
You are using Equatable but your props are empty. If you're using Equatable make sure to pass all properties to the props getter. (In both your state and event!)
Source: https://bloclibrary.dev/#/faqs?id=state-not-updating
The Flutter Todos Tutorial might also be helpful because it uses a filter too.

Using a bloc with Navigator 2.0

Hi I am trying to use a bloc instead of ChangeNotifierDelegate in my RouterDelegate class. Unfortunately the bloc is not being called when a route is changed through my routebloc, not sure why. I have tried wrapping the delegate in a BlocProvider, but it made no difference (I currently have it injected above in the main file.)
runApp(MyApp());
class _MyApp AppState extends State<MyApp> {
MyAppRouterDelegate _routerDelegate = MyAppRouterDelegate();
MyAppRouteInformationParser _routeInformationParser = MyAppRouteInformationParser();
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
lazy: false,
create: (context) => getIt<AuthBloc>()//..add(AppStarted()),
),
BlocProvider(
lazy: false,
create: (context) => getIt<RouterBloc>(),
),
],
child: MaterialApp.router(
title: 'MyApp',
theme: globalAppThemeData,
routerDelegate: _routerDelegate,
routeInformationParser: _routeInformationParser,
),
);
}
}
In my RouterDelegate I have .....
lass MyAppRouterDelegate extends RouterDelegate<MyAppConfiguration>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<MyAppConfiguration> {
final GlobalKey<NavigatorState> _navigatorKey;
String currentPage = '';
String selectedItem = '';
#override
GlobalKey<NavigatorState> get navigatorKey => _navigatorKey;
MyAppRouterDelegate() : _navigatorKey = GlobalKey<NavigatorState>();
#override
MyAppConfiguration get currentConfiguration {
currentPage = currentConfiguration.screen;
selectedItem = currentConfiguration.selectedItemId;
if (currentPage == UNKNOWN) {
return MyAppConfiguration.unknown();
} else if (currentPage == SPLASH) {
return MyAppConfiguration.splash();
} else if (currentPage == LOGIN) {
return MyAppConfiguration.login();
} else {
return MyAppConfiguration.unknown();
}
}
#override
Widget build(BuildContext context) {
List<Page> pages = [SplashPage(SPLASH)];
return BlocBuilder<RouterBloc, RouterState>(
builder: (context, state) {
if (state is ChangedRoute) {
pages.clear();
pages = state.pages;
}
return Navigator(
key: navigatorKey,
pages: pages,
onPopPage: (route, result) {
if (!route.didPop(result)) return false;
context.read<AuthBloc>().add(AuthEventLoggedOut());
return true;
},
);
},
);
}
#override
Future<void> setNewRoutePath(MyAppConfiguration configuration) async {
if (configuration.unknown) {
currentPage = UNKNOWN;
selectedItem = configuration.selectedItemId;
} else if (configuration.isSplashPage) {
currentPage = SPLASH;
selectedItem = configuration.selectedItemId;
} else if (configuration.isLoginPage) {
currentPage = LOGIN;
selectedItem = configuration.selectedItemId;
} else if (configuration.isSignUpPage)
currentPage = SIGNUP;
selectedItem = configuration.selectedItemId;
} else {
print(Constants.failureCouldNotSetRoute);
}
}
_clear() {
currentPage = UNKNOWN;
selectedItem = '';
}
}
In my app configuration...
class MyAppInformationParser
extends RouteInformationParser<MyAppConfiguration> {
#override
Future<MyAppConfiguration> parseRouteInformation(RouteInformation? routeInformation) async {
final uri = Uri.parse(routeInformation!.location!);
if (uri.pathSegments.length == 0) {
return MyAppConfiguration.splash();
} else if (uri.pathSegments.length == 1) {
final first = uri.pathSegments[1].toLowerCase();
if (first == LOGIN) {
return MyAppConfiguration.login();
} else {
return MyAppConfiguration.unknown();
}
} else {
return MyAppConfiguration.unknown();
}
}
#override
RouteInformation restoreRouteInformation(MyAppConfiguration configuration) {
if (configuration.isUnknownPage) {
return RouteInformation(location: '/unknown');
} else if (configuration.isSplashPage) {
return RouteInformation(location: '/splash');
} else if (configuration.isLoginPage) {
return RouteInformation(location: '/login');
} else {
return RouteInformation(location: '/unknown');
}
}
}
My auth bloc ...
#injectable
class AuthBloc extends Bloc<AuthEvent, AuthState> {
IAuthFacade authRepo;
RouterBloc routerBloc;
AuthBloc(this.authRepo, this.routerBloc) : super(Uninitialized());
#override
Stream<AuthState> mapEventToState(
AuthEvent event,
) async* {
if (event is AppStarted) {
yield AuthenticationLoading();
Option<CurrentUser> user = await authRepo.getSignedInUser();
yield user.fold(() {
routerBloc.add(RouterEventNewPage(pages: [LoginPage(LOGIN)]));
return Unauthenticated();
}, (user) {
routerBloc.add(RouterEventNewPage(pages: [HomePage(HOME)]));
return Authenticated(user);
});
}
if (event is AuthEventLoggedOut) {
authRepo.signOut();
///TODO: clear hive here??
}
}
}
abstract class AuthEvent extends Equatable {
#override
List<Object> get props => [];
}
//
class AppStarted extends AuthEvent {}
//
class AuthEventLoggedOut extends AuthEvent {}
abstract class AuthState extends Equatable {
#override
List<Object> get props => [];
}
//
class Uninitialized extends AuthState {}
//
class Authenticated extends AuthState {
final CurrentUser user;
Authenticated(this.user);
}
//
class Unauthenticated extends AuthState {}
//
class AuthenticationLoading extends AuthState {}
My Router Bloc...
#injectable
class RouterBloc extends Bloc<RouterEvent, RouterState> {
RouterBloc() : super(RouterInitial());
#override
Stream<RouterState> mapEventToState(
RouterEvent event,
) async* {
if (event is RouterEventNewPage) {
yield ChangingRoute();
yield ChangedRoute(pages: event.pages);
}
}
}
abstract class RouterEvent extends Equatable {
const RouterEvent();
#override
List<Object> get props => [];
}
class RouterEventNewPage extends RouterEvent {
final List<Page> pages;
RouterEventNewPage({required this.pages});
#override
List<Object> get props => [pages];
}
abstract class RouterState extends Equatable {
const RouterState();
#override
List<Object> get props => [];
}
class RouterInitial extends RouterState {}
class ChangingRoute extends RouterState {}
class ChangedRoute extends RouterState {
final List<Page> pages;
ChangedRoute({required this.pages});
#override
List<Object> get props => [pages];
}
The app runs through the Navigator in the build function of the delegate first, it navigates to the splash screen perfectly, then after my animation finishes in the splash screen it calls the auth bloc to check if user is authorised, this works perfectly which then calls the routerbloc. The router bloc adds the new login screen (as the user is logged out). However, the bloc inside the build function of the MyAppRouterDelegate is not firing again.
Any help provided would be very much appreciated.
When it runs through the MyAppRouterDelegates build function the first time I do receive the error
"
════════ Exception caught by scheduler library ═════════════════════════════════
The following StackOverflowError was thrown during a scheduler callback:
Stack Overflow
When the exception was thrown, this was the stack
#0 CrokettRouterDelegate.currentConfiguration
package:crokett/routes/crokett_router_delegate.dart:20
"
But I don't receive any more information on the error.
Don't you need a notifyListeners() somewhere in your blocBuilder after you update the page stack?
I am interested to know if you got it working.

Event added to Bloc and yield state but it not recall bloc builder

when I add an event from a stateless widget by using BlocProvider.of<>, it really adds event and yield state, and BlocBuilder work and change UI,
But, when adding an event from a separate class, it really adds an event to the bloc and onTransition work, but not yield a new state, and BlocBuilder not work to change UI.
the main :
main(){
return runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
MQTTManager x = MQTTManager();
return MaterialApp(
home: BlocProvider(
lazy: false,
create:(context)=>MqttblocBloc(MQTTManager())..add(StartConnect()) ,
child:Home() ,
),
);
}
}
the bloc :
class MqttblocBloc extends Bloc<MqttblocEvent, MqttblocState> {
MQTTManager manager = MQTTManager() ;
MqttblocBloc(this.manager) : super(MqttblocInitial());
#override
Stream<MqttblocState> mapEventToState(
MqttblocEvent event,
) async* {
if(event is StartConnect){
try{
manager.initializeMQTTClient();
manager.connect();
yield MqttblocInitial();
}catch(e){
print(e);
}
}
else if(event is ConnectedEvent) {
try{
print('inBloc connect....');
yield ConnectedState(MQTTManager.s);
}catch(e){
print(e);
}
}
else if(event is PublishedEvent){
try{
manager.publishsw1('on');
print('inBloc publish........');
yield PublishState(manager.getText1());
}catch(e){
print(e);
}
}
else if(event is DisconnectedEvent) {
try{
print('inBloc And Disconnnnn....');
yield DisconnectState(MQTTManager.u);
}catch(e){
print(e);
}
}
}
#override
void onTransition(Transition<MqttblocEvent, MqttblocState> transition) {
super.onTransition(transition);
print(transition);
}
}
and here separate class where I listen to server and add events to bloc :
class MQTTManager {
MqttblocBloc bloc ;
static var s ;
static var u ;
MqttServerClient client;
String text ;
String text1 ;
String text2 ;
static List<String> conn = [] ;
void initializeMQTTClient(){
client = MqttServerClient("broker.shiftr.io","User");
client.port = 1883;
client.secure = false;
client.logging(on: true);
client.onConnected = onConnected;
final MqttConnectMessage connMess = MqttConnectMessage()
.authenticateAs('889514b9', 'd5459e3f6b0422cb')
.withClientIdentifier("User")
.withWillTopic('willtopic')
.withWillMessage('My Will message')
.startClean() // Non persistent session for testing
.withWillQos(MqttQos.atLeastOnce);
print('EXAMPLE::Mosquitto client connecting....');
client.connectionMessage = connMess;
}
// Connect to the host
void connect() async{
assert(client != null);
try {
print('EXAMPLE::Mosquitto start client connecting....');
await client.connect();
Amar(); // <...... here calling this fun to start listen to Server
} on Exception catch (e) {
print('EXAMPLE::client exception - $e');
disconnect();
}
}
void disconnect() {
print('Disconnected');
client.disconnect();
}
void publishsw1(String message){
final MqttClientPayloadBuilder builder = MqttClientPayloadBuilder();
builder.addString(message);
client.publishMessage('hello/sw1', MqttQos.exactlyOnce, builder.payload);
}
void onConnected() {
print('EXAMPLE::shiftr client connected....');
client.subscribe("hello/sw1", MqttQos.atLeastOnce);
client.updates.listen((List<MqttReceivedMessage<MqttMessage>> c) {
final MqttPublishMessage recMess = c[0].payload;
final String pt =
MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
setText(pt);
});
}
Amar() { //<....... here h listen to server
bloc = MqttblocBloc(this);
client.subscribe("\$events", MqttQos.atLeastOnce);
client.updates.listen((List<MqttReceivedMessage<MqttMessage>> c) {
final MqttPublishMessage recMess = c[0].payload;
final String pt =
MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
var z = DetectEvent.fromJson(json.decode(pt));
if(z.type == 'connected'){
var connected = Connected.fromJson(json.decode(pt));
print (connected.type);
bloc.add(ConnectedEvent()); // <... here I add event to Bloc , but it not changeing UI
}
else if(z.type == 'disconnected'){
var disconnected = Disconnected.fromJson(json.decode(pt));
print (disconnected.type) ;
bloc.add(DisconnectedEvent()); // <... here I add event to Bloc , but it not changeing UI
}
else if(z.type == 'published'){
var published = Published.fromJson(json.decode(pt));
print(published.type) ;
}
}
}
and that is a stateless widget and use blocbuider :
class Home extends StatelessWidget {
MQTTManager v = MQTTManager();
#override
Widget build(BuildContext context) {
MqttblocBloc p = BlocProvider.of<MqttblocBloc>(context);
return Scaffold(
appBar: AppBar(
title: Text('Bloc MQTT'),
actions: [
Row(
children: [
IconButton(
icon: Icon(Icons.wb_incandescent,
),
onPressed: (){
p.add(PublishedEvent());//<.... here it change UI ,
},
),
],
)
],
),
body: Center(
child: BlocBuilder<MqttblocBloc,MqttblocState>(
// buildWhen: (previuosState , currentState)=>currentState.runtimeType !=previuosState.runtimeType,
builder:(context , state){
if(state is MqttblocInitial){
return CircularProgressIndicator();
}
else if(state is ConnectedState){
return IconButton(
icon: Icon(Icons.wifi),
onPressed: (){},
);
}
else if(state is PublishState){
return RaisedButton(
child: Text('${state.x}'),
onPressed: (){},
);
}
else if(state is DisconnectState){
return IconButton(
icon: Icon(Icons.wb_incandescent),
onPressed: (){
},
);
}
return CircularProgressIndicator();
} ,
),
)
);
}
}
bloc State :
#immutable
abstract class MqttblocState extends Equatable {
const MqttblocState();
#override
List<Object> get props => [];
}
class MqttblocInitial extends MqttblocState {}
class ConnectedState extends MqttblocState{
final String x ;
ConnectedState(this.x);
#override
List<Object> get props => [x];
}
class PublishState extends MqttblocState{
final String x ;
PublishState(this.x);
#override
List<Object> get props => [x];
}
class DisconnectState extends MqttblocState{
final String x ;
DisconnectState(this.x);
#override
List<Object> get props => [x];
}
and bloc events
#immutable
abstract class MqttblocEvent extends Equatable {
MqttblocEvent();
#override
List<Object> get props => [];
}
class StartConnect extends MqttblocEvent{}
class ConnectedEvent extends MqttblocEvent{}
class PublishedEvent extends MqttblocEvent{}
class DisconnectedEvent extends MqttblocEvent{}
The UI won't rebuild upon sending the same state again. You need to send some other state first. So for any event, your mapping should look like this:
if(event is StartConnect){
yield MqrrblocInProgress(); // <============== ADDED
try{
manager.initializeMQTTClient();
manager.connect();
yield MqttblocInitial();
}catch(e){
print(e);
}
Of course, you need to define this state (InProgress), too, as well as define some widget in the UI for this state (eg spinning wheel)
here is why, You're yielding the same state and using Equatable without being any different props to compare to so the BlocBuilder is not seeing any change.
You have two solutions ether un-inherit Equatable from class MqttblocEvent:
abstract class MqttblocEvent {
MqttblocEvent();
}
Or yield different state in-between like MqrrblocInProgress (Recommended) :
Stream<MqttblocState> mapEventToState(
MqttblocEvent event,
) async* {
yield MqrrblocInProgress();
.....

Managing state for onBackPressed in Flutter bloc

So I have a simple list that's clickable and goes to DetailScreen, issue I have is when I click back from the DetailScreen, how can I manage this state to save the last list?
Bloc
if (event is GetNews && !_hasReachedMax(state)) {
try {
if (currentState is NewsInitial) {
final news = await fetchNews(event.cat, pageNumber);
yield NewsLoaded(news, false);
}
if (currentState is NewsLoaded) {
pageNumber++;
final news = await fetchNews(event.cat, pageNumber);
yield news.isEmpty
? currentState.copyWith(hasReachedMax: true)
: NewsLoaded(currentState.node + news, false);
}
} catch (error) {
print(error);
yield NewsError("Error fetching news" + error);
}
} else if (event is GetDetailedNews) {
try {
final filter = await fetchDetailedNews(event.id);
yield DetailedNewsLoaded(filter);
} catch (error) {
yield NewsError("Couldn't fetch news : $error");
}
}
Attaching the event to the bloc
#override
void initState() {
super.initState();
_postBloc = BlocProvider.of<NewsBloc>(context)
..add(GetNews(widget.cat));
}
BlocBuilder
OnBackPressed I'm just stick in the else since I don't know how to manage the state
return BlocBuilder<NewsBloc, NewsState>(builder: (context, state) {
if (state is NewsLoaded) {
return ListView.builder(
controller: _scrollController,
itemCount: state.hasReachedMax
? state.node.length
: state.node.length + 1,
itemBuilder: (context, index) {
fullList = state.node;
print("list: ${state.node} \nlength: ${state.node
.length} \nindex: $index \n--------------");
return index >= state.node.length ?
BottomLoader() :
listViews(context, state.node[index], index);
});
}
else if (state is NewsError) {
return Center(
child: Container(
child: Text(state.message),
));
}
else {
return Center(child: CircularProgressIndicator(),);
}
});
States
abstract class NewsState extends Equatable {
const NewsState();
#override
List<Object> get props => [];
}
class NewsInitial extends NewsState {
const NewsInitial();
#override
List<Object> get props => [];
}
class NewsLoading extends NewsState {
const NewsLoading();
#override
List<Object> get props => [];
}
class NewsLoaded extends NewsState {
final List<Node> node;
final bool hasReachedMax;
NewsLoaded(this.node, this.hasReachedMax);
NewsLoaded copyWith({List<Node> node, bool hasReachedMax}) {
return NewsLoaded(node ?? this.node, hasReachedMax ?? this.hasReachedMax);
}
#override
List<Object> get props => [node];
}
class DetailedNewsLoaded extends NewsState {
final List<Node> node;
DetailedNewsLoaded(this.node);
#override
List<Object> get props => [node];
}
}
In the detail screen i add the GetDetailScreen event, and this event stays when onBackPressed
#override
void initState() {
BlocProvider.of<NewsBloc>(context)
..add(GetDetailedNews(widget.id));
super.initState();
}
I believe the problem is that your state when you press to see the article changes to DetailedNewsLoaded. So when you press back BlocBuilder<NewsBloc, NewsState> goes to the else state which returns the CircularProgressIndicator.
As i understand in your case you don't need the DetailedNewsLoaded state. You can just need to pass the state.node to DetailsScreen as a simple argument.
Why to do so much when you already have the hero widget in Flutter.
Make a List.
Use Hero animation for both list items and their details view.
Whenever the list item is clicked the details view will be shown.
When user presses back button, he/she will come to the position where that particular list item was.
So, basically you don't have to much.
I was going through some projects and I found this on github: https://github.com/whatsupcoders/Flutter-Hero-Widget
This project is walked through in this video that I found on YouTube: https://www.youtube.com/watch?v=mgSB5r11_Xw&t=15s
this project uses Hero widget, I hope it helps you.

Flutter Provider.of<> doesn't work in sub class

in my application this codes work fine when i use them inside flutter main classes as StatefulWidget, StatelessWidget or State
final User user = Provider.of<User>(context);
final ConnectivityStatus connection = Provider.of<ConnectivityService>(context).connectivityStatus;
for example:
class FragmentMainApplicationBodyState extends State<FragmentMainApplicationBody>{
final User user = Provider.of<User>(context);
final ConnectivityStatus connection = Provider.of<ConnectivityService>(context).connectivityStatus;
#override
Widget build(BuildContext context)
{
return Text('${user.userController.userInfo.name}');
}
}
now i'm trying use them inside sub-class, for example:
abstract class BaseState<T extends StatefulWidget> extends State {
bool isOnline = true;
ConnectivityStatus connection;
User user ;
#override
void initState() {
super.initState();
_initConnectivity();
}
Future _initConnectivity() async {
connection = Provider.of<ConnectivityService>(context).connectivityStatus;
isOnline = connection == ConnectivityStatus.Connected;
user = Provider.of<User>(context);
}
}
when i implementing them in the class, Providers doesn't work in this implementation
i get NULL for both of theme
class FragmentMainApplicationBodyState extends BaseState<FragmentMainApplicationBody>{
#override
Widget build(BuildContext context)
{
return Text('${user.userController.userInfo.name}');
}
}
MultiProvider structure:
ChangeNotifierProvider<User>.value(value: User()),
ChangeNotifierProvider<ConnectivityService>.value(value: ConnectivityService()),
User:
class User extends ChangeNotifier{
UserController userController;
}
class UserController {
UserInfo userInfo;
PageInfo pageInfo;
UserController(
{#required this.userInfo,
#required this.pageInfo,
});
}
ConnectivityService :
enum ConnectivityStatus { Connected, Disconnected }
class ConnectivityService extends ChangeNotifier {
ConnectivityStatus connectivityStatus;
ConnectivityService() {
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) async {
await _updateConnectionStatus().then((bool isConnected) {
connectivityStatus = isConnected ? ConnectivityStatus.Connected : ConnectivityStatus.Disconnected;
});
notifyListeners();
});
}
Future<bool> _updateConnectionStatus() async {
bool isConnected;
try {
final List<InternetAddress> result = await InternetAddress.lookup('google.com');
if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
isConnected = true;
}
} on SocketException catch (_) {
isConnected = false;
return false;
}
return isConnected;
}
}