Flutter BLoc pass parameters - flutter

I'm trying to pass parameters to a bloc event following the bloc pattern, I have found this article however my dart document couldn't find the dispatch (event) method.
Flutter BLoC - How to pass parameter to event?
How do I pass parameters to something like this
main.dart
this works
_counterBloc.add(Counter.increment);
But this doesn't
_counterBloc.add(Counter.increment(3));
bloc.dart
import 'package:bloc/bloc.dart';
enum CounterEvents { increment }
class CounterBloc extends Bloc<CounterEvents, int> {
#override
int get initialState => 0;
#override
Stream<int> mapEventToState(CounterEvents event) async* {
switch (event) {
case CounterEvents.increment:
print(event);
yield state + 1;
break;
}
}
}

you should write CounterEvent like below:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
enum EventStatus { INCREMENT, DECREMENT }
class CounterEvent {
final int value;
final EventStatus status;
const CounterEvent({#required this.value, #required this.status});
}
class CounterBLoC extends Bloc<CounterEvent, int> {
#override
int get initialState => 0;
#override
Stream<int> mapEventToState(event) async* {
if (event.status == EventStatus.INCREMENT) {
yield state + event.value;
} else if (event.status == EventStatus.DECREMENT) {
yield state - event.value;
}
}
}
and use them in the widget like below:
#override
Widget build(BuildContext context) {
final counterBloc = BlocProvider.of<CounterBLoC>(context);
return Scaffold(
body: Center(
child: BlocBuilder<CounterBLoC, int>(
builder: (ctx, state) {
return Text(
'count: $state',
style: TextStyle(fontSize: 28),
);
},
),
),
floatingActionButton: Align(
alignment: Alignment.bottomRight,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
onPressed: () {
counterBloc
.add(CounterEvent(value: 5, status: EventStatus.INCREMENT));
},
child: Icon(Icons.add_circle),
),
FloatingActionButton(
onPressed: () {
counterBloc
.add(CounterEvent(value: 5, status: EventStatus.DECREMENT));
},
child: Icon(Icons.remove_circle),
),
],
),
),
);
}
make sure to init your bloc in the main :
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider<CounterBLoC>(
create: (ctx) => CounterBLoC(),
child: TestBlocWidget(),
),
);
}
}

If you are trying to rebuild the Counter App using bloc patter,
Go through this article this may help.
https://bloclibrary.dev/#/fluttercountertutorial?id=counter-app

Consider making a custom event. Your solution should be something like this (haven't checked for bugs, but the paradigm is correct):
abstract class CounterEvent {}
class Increment extends CounterEvent {
int amount;
Increment({this.amount});
}
Then in your bloc:
class CounterBloc extends Bloc<CounterEvent, int> {
#override
int get initialState => 0;
#override
Stream<int> mapEventToState(CounterEvent event) async* {
if (event is Increment) {
yield state + event.amount;
}
}
}

Related

Flutter: Error says - Could not find a generator for route RouteSettings while trying to navigate to another screen

Although questions with such error messages exist in this site, none solves my problem.
I have a button and on clicking the button, I just need to go to a different screen. But when ever I tap on the screen, the error shows up.
I first setup a route in MaterialApp and then tried to navigate to that route on tapping the button. The full code and the error message are given below:
Code:
import 'livesession1to1.dart';
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(
home: CountDownTimer(),
navigatorKey: NavigationService.navigatorKey, // set property// Added by me later from prev project
// initialRoute: "/",
routes: <String, WidgetBuilder> {
'/liveSession1to1': (context) =>LiveSession1to1(),
},
)
);
}// end of main
class CountDownTimer extends StatefulWidget {
const CountDownTimer();
final String? title='';
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer> {
#override
void initState() {
super.initState();
}// end of initstate
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Live Session'),
),
body: Text('Demo Text'),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_button(title: "Go", onPressed: () =>
Navigator.of(context ,rootNavigator: true).pushNamed('/liveSession1to1', arguments: {'room_found': 123 } )
),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
Widget _button({required String title, VoidCallback? onPressed}) {
return Expanded(
child: TextButton(
child: Text(
title,
style: const TextStyle(color: Colors.white),
),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red),
),
onPressed: onPressed,
));
}
}
Error found:
The following assertion was thrown while handling a gesture:
Could not find a generator for route RouteSettings("/liveSession1to1", {room_found: 123}) in the _WidgetsAppState.
Make sure your root app widget has provided a way to generate
this route.
Generators for routes are searched for in the following order:
For the "/" route, the "home" property, if non-null, is used.
Otherwise, the "routes" table is used, if it has an entry for the route.
Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "home" and "routes".
Finally if all else fails onUnknownRoute is called.
Unfortunately, onUnknownRoute was not set.
So how to solve the problem ?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
void main() {
locatorSetup();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final _navService = locator<NavigationHandler>();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
onGenerateRoute: generateRoute,
navigatorKey: _navService.navigatorKey,
// I don't know what your first screen is, so I'm assuming it's a Splash Screen
home: SplashScreen());
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final _navService = locator<NavigationHandler>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_navService.pushNamed(Routes.LiveSession1to1);
},
child: Text("Go to next page"),
),
));
}
}
class LiveSession1to1 extends StatefulWidget {
const LiveSession1to1({Key? key}) : super(key: key);
#override
State<LiveSession1to1> createState() => _LiveSession1to1State();
}
class _LiveSession1to1State extends State<LiveSession1to1> {
final _navService = locator<NavigationHandler>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_navService.goBack();
},
child: Text("Go to previous page"),
),
));
}
}
GetIt locator = GetIt.instance;
void locatorSetup() {
locator
.registerLazySingleton<NavigationHandler>(() => NavigationHandlerImpl());
}
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case Routes.LiveSession1to1:
return _getPageRoute(view: LiveSession1to1(), routeName: settings.name);
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}'),
),
),
);
}
}
PageRoute _getPageRoute({String? routeName, Widget? view}) {
return MaterialPageRoute(
settings: RouteSettings(
name: routeName,
),
builder: (_) => view!,
);
}
class Routes {
static const String LiveSession1to1 = "liveSession1to1";
}
abstract class NavigationHandler {
///Pushes `destinationRoute` route onto the stack
Future<dynamic>? pushNamed(String destinationRoute, {dynamic arg});
///Pushes `destinationRoute` onto stack and removes stack items until
///`lastRoute` is hit
Future<dynamic>? pushNamedAndRemoveUntil(
String destinationRoute, String lastRoute,
{dynamic arg});
///Pushes `destinationRoute` onto stack with replacement
Future<dynamic>? pushReplacementNamed(String destinationRoute, {dynamic arg});
///Pushes `destinationRoute` after popping current route off stack
Future<dynamic>? popAndPushNamed(String destinationRoute, {dynamic arg});
///Pops current route off stack
void goBack();
///Pops routes on stack until `destinationRoute` is hit
void popUntil(String destinationRoute);
///Exits app
void exitApp();
late GlobalKey<NavigatorState> navigatorKey;
}
/// Handles navigation
class NavigationHandlerImpl implements NavigationHandler {
#override
late GlobalKey<NavigatorState> navigatorKey;
/// Constructs a NavigationHandler instance
NavigationHandlerImpl({GlobalKey<NavigatorState>? navigatorKey}) {
this.navigatorKey = navigatorKey ?? GlobalKey<NavigatorState>();
}
NavigatorState? get state => navigatorKey.currentState;
#override
void exitApp() {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
}
#override
void goBack() {
if (state != null) {
return state!.pop();
}
}
#override
Future? popAndPushNamed(String destinationRoute, {arg}) {
if (state != null) {
return state!.popAndPushNamed(destinationRoute, arguments: arg);
}
}
#override
void popUntil(String destinationRoute) {
if (state != null) {
return state!.popUntil(ModalRoute.withName(destinationRoute));
}
}
#override
Future? pushNamed(String destinationRoute, {arg}) {
if (state != null) {
return state!.pushNamed(destinationRoute, arguments: arg);
}
}
#override
Future? pushNamedAndRemoveUntil(String destinationRoute, String lastRoute,
{arg}) {
if (state != null) {
return state!.pushNamedAndRemoveUntil(
destinationRoute,
ModalRoute.withName(lastRoute),
arguments: arg,
);
}
}
#override
Future? pushReplacementNamed(String destinationRoute, {arg}) {
if (state != null) {
return state!.pushReplacementNamed(destinationRoute, arguments: arg);
}
}
}

Flutter app: hide appBar action according to ApplicationState value

I'm quite new to Flutter and I think I haven't understand all the logic behind the state management with Providers.
I've the following widget:
class App extends StatelessWidget {
List<IconButton> navigationActions(BuildContext context) {
return
Consumer<ApplicationState>(builder: (context, appState, _) {
if (appState.loginState == 'loggedIn') {
return [IconButton(
icon: const Icon(Icons.logout),
tooltip: 'Logout',
onPressed: () {
context.read<ApplicationState>().signOut();
},
)];
}
})
;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FOO'),
actions: navigationActions(context)
),
body: ListView(
.........
)
)
}
And I want to show/hide the AppBar action according to the flag loginState set inside ApplicationState
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => ApplicationState(),
builder: (context, _) => App(),
),
);
}
class ApplicationState extends ChangeNotifier {
ApplicationState() {
init();
}
String _loginState = 'loggedOut';
String get loginState => _loginState;
}
I'm not sure about how to implement the function navigationActions.
Which should be the return type? Since I'm not returning a data in the else branch I'm not sure about how to manage that type.
Maybe there are smarter solution, I don't know yet.. Someone has ever implemented a similar logic with Providers?
navigationActions has to return List<IconButton>, but you are returning the result of Consumer which is a Widget. You can use other methods to get the ApplicationState. Here is example code which does what you want:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ApplicationState extends ChangeNotifier {
String _loginState = 'loggedIn';
set loginState(String state) {
_loginState = state;
}
get loginState => _loginState;
void toggleState() {
if (loginState == 'loggedIn')
loginState = 'loggedOut';
else
loginState = 'loggedIn';
notifyListeners();
}
void signOut() {}
}
class ActionTest extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ApplicationState(),
child: ActionApp(),
);
}
}
class ActionApp extends StatelessWidget {
List<IconButton> navigationActions(BuildContext context) {
final appState = Provider.of<ApplicationState>(context);
if (appState.loginState == 'loggedIn') {
return [
IconButton(
icon: const Icon(Icons.logout),
tooltip: 'Logout',
onPressed: () {
appState.signOut();
},
)
];
} else {
return [];
}
}
#override
Widget build(BuildContext context) {
final appState = Provider.of<ApplicationState>(context);
return Scaffold(
appBar: AppBar(title: Text('FOO'), actions: navigationActions(context)),
body: Container(),
floatingActionButton: FloatingActionButton(
onPressed: () {
appState.toggleState();
},
child: Icon(appState.loginState == 'loggedIn'
? Icons.toggle_off
: Icons.toggle_on),
),
);
}
}

Why BlocBuilder is stuck in the initial state while using get_it?

I'm using flutter_bloc to manage the states of my app, and get_it to inject the needed dependencies following the idea suggested by the Reso Coder's Flutter Clean Architecture Proposal.
Everything is working fine except that the bloc is not changing its state (it's stuck in the initial state)
Here is the code of the involved classes:
The States
abstract class PaintingsState extends Equatable {
final properties = const <dynamic>[];
PaintingsState([properties]);
#override
List<Object> get props => [properties];
}
class PaintingsLoading extends PaintingsState {}
class PaintingsLoaded extends PaintingsState {
final PaintingCardItems cardItems;
PaintingsLoaded({#required this.cardItems}) : super([cardItems]);
}
class Error extends PaintingsState {
final String message;
Error({#required this.message}) : super([message]);
}
The Events
abstract class PaintingsEvent extends Equatable {
const PaintingsEvent();
#override
List<Object> get props => [];
}
/// Tells the bloc that it needs to load the paintings from the PaintingsRepository
class GetPaintings extends PaintingsEvent {}
The Bloc
const String FILE_NOT_FOUND_MESSAGE = 'FileNotFound Failure';
class PaintingsBloc extends Bloc<PaintingsEvent, PaintingsState> {
final GetPaintingCardItems getCardItems;
PaintingsBloc({#required this.getCardItems}) : super(PaintingsLoading());
#override
Stream<PaintingsState> mapEventToState(PaintingsEvent event) async* {
if (event is GetPaintings) {
yield* _mapGetPaintingsToState();
}
}
Stream<PaintingsState> _mapGetPaintingsToState() async* {
yield PaintingsLoading();
final failureOrPaintingCardItems = await getCardItems(NoParams());
yield failureOrPaintingCardItems.fold(
(failure) => Error(message: _mapFailureToMessage(failure)),
(paintingCardItems) => PaintingsLoaded(cardItems: paintingCardItems));
}
String _mapFailureToMessage(Failure failure) {
switch (failure.runtimeType) {
case FileNotFound:
return FILE_NOT_FOUND_MESSAGE;
default:
return 'Unexpected error';
}
}
}
Dependencies injection
/// Ambient variable to access the service locator
final sl = GetIt.instance;
/// Set up all the objects you want to access later through the service locator [sl]
void setUpServiceLocator() {
initFeatures();
}
void initFeatures() {
//! Features - Paintings
// Bloc
sl.registerLazySingleton<PaintingsBloc>(() => PaintingsBloc(getCardItems: sl<GetPaintingCardItems>()));
// Use cases
sl.registerLazySingleton<GetPaintingCardItems>(() => GetPaintingCardItems(sl<PaintingsRepository>()));
// Repository
sl.registerLazySingleton<PaintingsRepository>(
() => PaintingsRepositoryImpl(dataSource: sl<PaintingsDataSource>()));
// Data sources
sl.registerLazySingleton<PaintingsDataSource>(() => PaintingsDataSourceImpl());
}
main.dart
void main() {
// dependencies injection
setUpServiceLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<PaintingsBloc>(
create: (_) => sl<PaintingsBloc>(),
child: MaterialApp(
title: 'My Paintings',
theme: appTheme,
initialRoute: '/',
onGenerateRoute: RouteGenerator.generateRoute,
),
);
}
}
Page where I use BlocBuilder
class PaintingsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
...
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Stack(
children: <Widget>[
SafeArea(
child: Column(
...
BlocBuilder<PaintingsBloc, PaintingsState>(
builder: (context, state) {
if(state is PaintingsLoading) {
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
} else if(state is PaintingsLoaded) {
List<PaintingCardItem> _list = state.cardItems.paintingCardItems;
return Expanded(
child: SizedBox(
child: _list.length != 0
? ListCardView(
cardItems: _list)
: Container(
child: Center(child: Text('Empty list'))),
),
);
} else if(state is Error){
return Container(
child: Center(child: Text(state.message)));
} else {
return Container(
child: Center(child: Text('Unknown Error')));
}
}
)
],
))
],
),
),
);
}
}
So, somehow the state of the bloc does not change from PaintingsLoading to either PaintingsLoaded or Error.
If someone can give me some idea to solve this problem, I will really appreciate it.
I solved it, I just needed to add the event to the bloc. So, my solution was to create another state called PaintingsInitialState like so:
The States
...
class PaintingsInitialState extends PaintingsState {}
...
Then in the Bloc, I just changed the constructor of the bloc.
PaintingsBloc({#required this.getCardItems}) : super(PaintingsInitialState());`
Finally, I added the following condition inside the builder parameter of the BlocBuilder.
if (state is PaintingsInitialState) {
_paintingsBloc.add(GetPaintings());
}
I think that the information provided in the offitial site of the bloc library can be useful to understand how to use bloc pattern and libraries properly - particularly Flutter Wheather Tutorial.

Delete specific widget | Flutter & Riverpod

As shown in the image, I'm trying to have a list of dice where I can add or delete a die. I've tried StateProvider, ChangeNotifier, and StateNotifier. Each one doesn't seem to work as I expect it to. I'm trying to make a provider that contains a list of dieWidgets, but I can't figure out how to remove a specific die when I longpress on it. The image shows a popup menu to delete it, that's the long-term goal, but just a longpress delete would be good for now. Thoughts on how to approach this?
Code
main.dart
class DiceNotifier extends ChangeNotifier {
List<DieWidget> dice = [];
void add() {
dice.add(DieWidget());
notifyListeners();
}
void removeDie(int id) {
// FIXME: Unable to delete a die based on id
print(id);
notifyListeners();
}
}
final diceProvider = ChangeNotifierProvider((_) {
return DiceNotifier();
});
class MyHomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final dice = watch(diceProvider).dice;
return Scaffold(
appBar: AppBar(
title: Text("Dice"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
...dice,
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(diceProvider).add();
},
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
die_widget.dart
class DieWidget extends StatefulWidget {
#override
_DieWidgetState createState() => _DieWidgetState();
}
class _DieWidgetState extends State<DieWidget> {
int value = 0;
int id = 0;
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
'$value',
),
onPressed: () {
setState(() {
value++;
id++;
});
// context.read(dieProvider).increment();
},
onLongPress: () {
final dice = context.read(diceProvider);
dice.removeDie(id);
// print(this.value);
},
);
}
}
One solution would be to define a parameter value in the DiceWidget class:
class DiceWidget extends StatefulWidget {
const DiceWidget({ Key key, this.value }) : super(key: key);
int value;
#override
_DiceWidgetState createState() => _DiceWidgetState();
}
And access this data from the DiceWidget:
class DiceWidget extends StatefulWidget {
#override
_DiceWidgetState createState() => _DiceWidgetState();
}
class _DiceWidgetState extends State<DiceWidget> {
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
widget.value.toString() ?? '',
),
onLongPress: () {
final dice = context.read(diceProvider);
dice.removeDice(widget.value);
// print(widget.value);
},
);
}
}
In the DiceNotifier class, I'd recommend to implement the dices array as a List<int>:
List<int> dices = [];
Therefore, the addDice() and removeDice() functions will be, respectively:
class DiceNotifier extends ChangeNotifier {
List<int> dices = [];
void addDice() {
dices.add(dices.length);
notifyListeners();
}
void removeDice(int id) {
dices.remove(id);
print(id);
notifyListeners();
}
}
To make the example work, we need to modify the MyHomePage Column children as well, to build the list of DiceWidgets:
...dices.map((d) => DiceWidget(value: d)).toList(),
The whole example will then be:
main.dart:
class DiceNotifier extends ChangeNotifier {
List<int> dices = [];
void addDice() {
dices.add(dices.length);
notifyListeners();
}
void removeDice(int id) {
dices.remove(id);
print(id);
notifyListeners();
}
}
final diceProvider = ChangeNotifierProvider((_) {
return DiceNotifier();
});
class MyHomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, ScopedReader watch) {
final dices = watch(diceProvider).dices;
return Scaffold(
appBar: AppBar(
title: Text("Dice"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
...dices.map((d) => DiceWidget(value: d)).toList(),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read(diceProvider).addDice();
},
child: Icon(Icons.add),
),
);
}
}
dice_widget.dart:
class DiceWidget extends StatefulWidget {
#override
_DiceWidgetState createState() => _DiceWidgetState();
}
class _DiceWidgetState extends State<DiceWidget> {
#override
Widget build(BuildContext context) {
return FlatButton(
child: Text(
widget.value.toString() ?? '',
),
onLongPress: () {
final dice = context.read(diceProvider);
dice.removeDice(widget.value);
print(widget.value);
},
);
}
}

Error BlocProvider.of() called with a context That doesn't contain A bloc of type UserBloc

Anyone know what should I do with this? I making a user's listview, it should show appbar, tabbar userlist, user group, and the list of all users.
When I'm not using model and repository and just put the data manually on the users_page.dart, everything was fine, the layout showing. But it's happen idk what to do.
If you want to get the BLoC from a BlocProvider.of(context) you need to provide that BLoC somewhere on top of your current context. You need something like this:
BlocProvider(
create: (BuildContext context) => UsersBloc(),
child: child(),
);
In one of the ancestors of your UsersPage. Basically you need to tell from where to get an instance of your BLoC in that widget tree. Normally i write those BlocProviders in the MaterialApp, ensuring that every page will have those blocs in theirs context.
this is my UsersBloc:
import 'package:bloc/bloc.dart';
import 'package:merchant/feature/bloc/users/users_event.dart';
import 'package:merchant/feature/bloc/users/users_state.dart';
class UsersBloc
extends Bloc < UsersEvent, UsersState > {
int currentIndex = 0;
#override
UsersState get initialState => UsersLoading();
#override
Stream < UsersState > mapEventToState(UsersEvent event) async * {
if (event is UsersStarted) {
this.add(UsersTapped(index: this.currentIndex));
}
if (event is UsersTapped) {
this.currentIndex = event.index;
yield CurrentIndexChanged(currentIndex: this.currentIndex);
yield UsersLoading();
if (this.currentIndex == 0) {
// String data = await UsersRepository();
yield UsersListLoaded(text: "UsersList");
}
if (this.currentIndex == 1) {
// String data = await UsersRepository();
yield UsersGroupLoaded(text: "UsersGroup");
}
if (event is UsersDetailTapped) {
yield UsersDetailLoaded(text: "UserDetail");
}
}
}
}
this is my UsersPage :
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:merchant/component/widget/loading_widget.dart';
import 'package:merchant/feature/bloc/users/users_bloc.dart';
import 'package:merchant/feature/bloc/users/users_event.dart';
import 'package:merchant/feature/bloc/users/users_state.dart';
import 'package:merchant/feature/splash_page.dart';
import 'package:merchant/feature/ui/users/users_list_tab.dart';
import 'package:merchant/feature/ui/users/users_group_tab.dart';
class UsersPage extends StatelessWidget {
final String text;
const UsersPage(this.text): super();
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text(
"Users (UKSW)",
style: TextStyle(color: Colors.white),
),
bottom: TabBar(
onTap: (index) => BlocProvider.of < UsersBloc > (context).add(UsersTapped(index: index)),
isScrollable: true,
labelColor: Colors.white,
unselectedLabelColor: Colors.black54,
tabs: [
Tab(text: "User List"),
Tab(text: "User Group"),
],
),
actions: [
IconButton(icon: Icon(Icons.search),
onPressed: () {}
)
]
),
body: BlocBuilder < UsersBloc, UsersState >
( //bloc: BlocProvider.of<UsersBloc>(context),
builder: (context, state) {
if (state is UsersLoading) {
return LoadingWidget(visible: true);
}
if (state is UsersListLoaded) {
return UsersList();
} else if (state is UsersGroupLoaded) {
return UsersGroup();
}
return SplashPage();
}
)
));
}
}
this is my users_model.dart :
import 'dart:convert';
class UsersModel {
String name, username;
UsersModel({
this.name,
this.username
});
factory UsersModel.fromJson(Map<String, dynamic> json) => UsersModel(
name: json['name'],
username: json['username']
);
}
can you try initialising your bloc at the root of your app something like this
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider< UsersBloc >(
create: (context) => UsersBloc(),
child: UsersPage(),
),
);
}
}
then in your UsersPage file initialise bloc and assign it to variable and use it across.
Widget build(BuildContext context) {
final UsersBloc usersBloc = BlocProvider.of<UsersBloc>(context);
return DefaultTabController(
length: 2,
...
bottom: TabBar(
onTap: (index) => usersBloc.add(UsersTapped(index: index),
...)
}
I have the same problem if you are calling from a non-bloc (Normal class) to a new bloc class without any bloc changes to the main class (Multi-bloc provider to runApp). Then add
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
BlocProvider<UsersBloc>(
create: (context) => UsersBloc(),
child: UsersBlocPage(),
),
));
This to your calling sequence. It works for me