I am new to Flutter, and bloc too. I got the idea, how bloc works. But When I create a simple app as the first step of my note app. The bloc doesn't give the data. This simple app has two screens. list screen and Notedetailscreen. Button in NoteDetailScreen tapped, data does not print to the text widget.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => NoteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
note_bloc.dart
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
part 'note_event.dart';
part 'note_state.dart';
class NoteBloc extends Bloc<NoteblocEvent, NoteblocState> {
NoteBloc() : super(NoteblocInitial());
#override
Stream<NoteblocState> mapEventToState(
NoteblocEvent event,
) async* {
if (event == NoteSaveEvent) {
yield NoteSaveState(state);
}
}
}
part of 'note_bloc.dart';
#immutable
abstract class NoteblocEvent {}
class NoteSaveEvent extends NoteblocEvent {
NoteSaveEvent(this.text);
final text;
}
note_state.dart
part of 'note_bloc.dart';
#immutable
abstract class NoteblocState {}
class NoteblocInitial extends NoteblocState {}
class NoteSaveState extends NoteblocState {
NoteSaveState(this.text);
final text;
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NoteDetailScreen(),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
Your bloc, state, and event looks fine. When you push screen you might need to use BlocProvider again. So try this:
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
NoteBloc _noteBloc = NoteBloc();
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => _noteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(value: BlocProvider.of<NoteBloc>(context), child: NoteDetailScreen()),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
bloc: BlocProvider.of<NoteBloc>(context),
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
So, this is not an answer of your question but consider that as alternative (for future users of SO).
As state management is a free choice, and everyone could manage that as it’s “modus operandi“ this helper class “home made” could be a good choice.
import 'dart:async';
import 'dart:core';
class Method {
Method(this.name, this.params);
final String name;
final Map<String, Object> params;
}
class _Controller {
_Controller._();
static final Map<String, _Controller> _this = new Map<String, _Controller>();
final Map<String, Function(Method)> _funcs = new Map<String, Function(Method)>();
factory _Controller(String identifier) => _this.putIfAbsent(identifier, () => _Controller._());
Future<void> activateListener(String listenerId, Function(Method) function) async {
if (function != null)
_funcs.containsKey(listenerId) ? _funcs[listenerId] = function : _funcs.putIfAbsent(listenerId, () => function);
}
Future<void> deactivateListener(String listenerId) async =>
_funcs.removeWhere((String key, Function(Method) func) => key == listenerId);
Future<void> removeListener(String identifier) async =>
_this.removeWhere((String key, _Controller mClass) => key == identifier);
Future<void> callMethod(String methodName, {Map<String, Object> params}) async =>
Future.forEach(_funcs.values.where((v) => v != null), (func) async => func.call(Method(methodName, params)));
}
mixin MethodListener on Object {
_Controller _getController(String identifier) => _Controller(identifier ?? this.runtimeType.toString());
Future<void> activateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach(
(String currentId) => _getController(currentId).activateListener(this.hashCode.toString(), onMethodListener));
else
_getController(identifier).activateListener(this.hashCode.toString(), onMethodListener);
}
Future<void> deactivateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach((String currentId) => _getController(currentId).deactivateListener(this.hashCode.toString()));
else
_getController(identifier).deactivateListener(this.hashCode.toString());
}
Future<void> removeListener({String identifier}) async => _getController(identifier).removeListener(identifier);
void onMethodListener(Method method) async => null;
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
_getController(identifier).callMethod(methodName, params: params);
}
class MethodManager with MethodListener {
MethodManager._();
static MethodManager _this;
factory MethodManager() {
if (_this == null) _this = MethodManager._();
return _this;
}
Future<void> callMethodOnWidgets(List<String> identifiers, String methodName, {Map<String, Object> params}) async =>
identifiers.forEach((String currentId) => callMethodOn(currentId, methodName, params: params));
#override
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
super.callMethodOn(identifier, methodName, params: params);
}
then you can implements classes with “with MethodListener” as follows:
import 'package:flutter/material.dart';
import 'package:yourpackagehere/utils/XMethods.dart';
class Test extends StatefulWidget {
static const String NAME = "Test";
#override
createState() => _TestState();
}
class _TestState extends State<Test> with MethodListener {
String _ciao;
#override
void initState() {
super.initState();
this.activateListener(identifier: Test.NAME);
}
#override
void dispose() {
this.deactivateListener(identifier: Test.NAME);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(child: Text(_ciao));
}
#override
void onMethodListener(Method method) {
switch (method.name) {
case "say_hello":
if (mounted) {
setState(() {
_ciao = method.params["my_string"];
});
}
break;
}
}
}
Usage:
From everywhere (from widgets or classes):
MethodManager().callMethodOn(Test.NAME, "say_hello", params: {"my_string": "SIAMO CAMPIONI DI EUROPA!!!"});
Related
I saved the data using SharedPreferences, when my app is starting i try check type of user "client" or "company" after that wil be shown screen.
Future<void> setTypeClient() async {
final _storage = SharedPreferences.getInstance();
final storage = await _storage;
storage.setString('type_user_db', 'client');
}
My app is starting
I have variable
var typeUser = ' ' ; and use it when I determine which screen to display
Аfter I get the data from SharedPreferences, I need to put value in this variable
But i have an error, cause variable remains empty
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../views/home_view.dart';
import '../views/auth/client_auth_view.dart';
import '../views/auth/company_auth_view.dart';
import '../views/auth/company_reg_view.dart';
import '../views/client/client_view.dart';
import '../views/company/company_view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MyAppBody();
}
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
#override
State<MyAppBody> createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
Future<void> getTypeUser() async {
final storage = await SharedPreferences.getInstance();
final type = storage.getString('type_user_db');
setState(() {
typeUser = type!;
});
}
var typeUser = '';
Map<String, Widget> screenByUser = {
'client': const ClientView(),
'company': const CompanyView(),
};
bool isLogOut = true;
#override
void initState() {
if (FirebaseAuth.instance.currentUser != null) {
getTypeUser();
setState(() {
isLogOut = false;
});
super.initState();
} else {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Well Plus',
debugShowCheckedModeBanner: false,
routes: {
'/main': (context) => const HomeView(),
'/auth_client': (context) => const AuthClientView(),
'/auth_company': (context) => const AuthCompanyView(),
'/reg_company': (context) => const RegCompanyView(),
'/client': (context) => const ClientView(),
'/company': (context) => const CompanyView(),
},
home: isLogOut ? const HomeView() : screenByUser[typeUser],
);
}
}
update
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../views/home_view.dart';
import '../views/auth/client_auth_view.dart';
import '../views/auth/company_auth_view.dart';
import '../views/auth/company_reg_view.dart';
import '../views/client/client_view.dart';
import '../views/company/company_view.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MyAppBody();
}
}
class MyAppBody extends StatefulWidget {
const MyAppBody({Key? key}) : super(key: key);
#override
State<MyAppBody> createState() => _MyAppBodyState();
}
class _MyAppBodyState extends State<MyAppBody> {
Future<void> getTypeUser() async {
final storage = await SharedPreferences.getInstance();
final type = storage.getString('type_user_db');
setState(() {
typeUser = type!;
});
}
var typeUser = '';
Map<String, Widget> screenByUser = {
'client': const ClientView(),
'company': const CompanyView(),
};
bool get isLogOut => typeUser.isEmpty;
#override
void initState() {
if (FirebaseAuth.instance.currentUser == null) {
getTypeUser();
setState(() {
isLogOut = false;
});
super.initState();
} else {
return;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Well Plus',
debugShowCheckedModeBanner: false,
routes: {
'/main': (context) => const HomeView(),
'/auth_client': (context) => const AuthClientView(),
'/auth_company': (context) => const AuthCompanyView(),
'/reg_company': (context) => const RegCompanyView(),
'/client': (context) => const ClientView(),
'/company': (context) => const CompanyView(),
},
home: isLogOut ? const HomeView() : screenByUser[typeUser],
);
}
}
error in
setState(() {
isLogOut = false;
});
error screen
enter image description here
To solve this issue we have to clarify synchronous and asynchronous operations.
Initialating you widget with initState and next build method called one by one in synchronous way but getUser is asynchronous.
This is meant that you specify isLogOut before you setting the typeUser.
Instead of manually setting the isLogOut property just to change it to getter
bool get isLogOut => typeUser.isEmpty;
And you can guarantee the property will be correct all the time when you requests it.
Enjoy coding
How to access counterProvider inside extension?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
main() {
runApp(const ProviderScope(child: MyApp2()));
}
class MyApp2 extends ConsumerWidget {
const MyApp2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final watchCounter = ref.watch(counterProvider);
return MaterialApp(
home: Scaffold(
body: Text('$watchCounter'),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
ref.read(counterProvider.notifier).adder();
},
),
),
);
}
}
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
int counter = 0;
void adder() {
state = ++counter;
}
}
final counterProvider = StateNotifierProvider<CounterNotifier, int>(
(ref) => CounterNotifier(),
);
extension StringExtension on String {
String AddPrefix(){
// <---***--- How to access counterProvider here?
return '#$this';
}
}
You can pass it as a parameter to the AddPrefix() function.
Send to addPrefix() extension with function params.
extension StringExtension on String {
String addPrefix(String prefix){
return prefix + ' #$this';
}
}
Text('$watchCounter'.addPrefix("test")) // test 0
I tried to follow the answer to this question, but I was not able to make it work.
I reproduced my issue on the counter app, changing it as follow.
main.dart
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (BuildContext ctx) => DummyCubit(),
),
],
child: MaterialApp(
...
}
class _MyHomePageState extends State<MyHomePage> {
...
#override
Widget build(BuildContext context) {
return Scaffold(
...
body: Center(
child: CounterViewer(counter: _counter),
),
...
);
}
}
class CounterViewer extends StatelessWidget {
const CounterViewer({required this.counter, Key? key}) : super(key: key);
final int counter;
#override
Widget build(BuildContext context) {
return BlocBuilder<DummyCubit, AState>(
builder: (ctx, state) => (state is! StateLoaded)
? const CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
dummy_cubit.dart
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
class DummyCubit extends Cubit<AState> {
DummyCubit() : super(const InitState());
Future<void> executeLogic() async {
emit(const StateLoading());
// do some logic
emit(StateLoaded('some data'));
}
}
#immutable
abstract class AState {
const AState();
}
class InitState extends AState {
const InitState();
}
class StateLoading extends AState {
const StateLoading();
}
class StateLoaded extends AState {
const StateLoaded(this.data);
final String data;
#override
String toString() => data.toString();
#override
bool operator ==(Object other) =>
identical(this, other) ||
(other is StateLoaded &&
runtimeType == other.runtimeType &&
data == other.data);
#override
int get hashCode => data.hashCode;
}
widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:mocktail/mocktail.dart' as mocktail;
import 'package:counter/dummy_cubit.dart';
import 'package:counter/main.dart';
class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}
class AStateFake extends Fake implements AState {}
final dummyCubit = MockDummyCubit();
Widget get counter => MultiBlocProvider(
providers: [
BlocProvider<DummyCubit>(
create: (BuildContext ctx) => dummyCubit,
),
],
child: const MaterialApp(
home: CounterViewer(counter: 1),
),
);
void main() {
setUpAll(() {
mocktail.registerFallbackValue(AStateFake());
});
group('Counter viewer', () {
mocktail.when(() => dummyCubit.state).thenReturn(InitState());
testWidgets('should build', (WidgetTester tester) async {
await tester.pumpWidget(counter);
});
});
}
When running the test, I get this error:
The following StateError was thrown running a test:
Bad state: No method stub was called from within `when()`. Was a real method called, or perhaps an
extension method?
And removing the mocktail.when line, I get this error:
The following _TypeError was thrown building CounterViewer:
type 'Null' is not a subtype of type 'AState'
How do I solve this issue?
How do I control which state is emitted by my DummyCubit?
After reading this, I found the solution
class MockDummyCubit extends MockCubit<AState> implements DummyCubit {}
class AStateFake extends Fake implements AState {}
void main() {
late MockDummyCubit dummyCubit;
setUpAll(() {
mocktail.registerFallbackValue(AStateFake());
});
setUp(() {
dummyCubit = MockDummyCubit();
mocktail.when(() => dummyCubit.state).thenReturn(const InitState());
});
group('Counter viewer', () {
testWidgets('should build', (WidgetTester tester) async {
await tester.pumpWidget(getCounter(dummyCubit));
});
});
}
Widget getCounter(MockDummyCubit dummyCubit) => MultiBlocProvider(
providers: [
BlocProvider<DummyCubit>(
create: (BuildContext ctx) => dummyCubit,
),
],
child: const MaterialApp(
home: CounterViewer(counter: 1),
),
);
I've just started with Flutter recently. BLoC does indeed has a steep learning curve...
As it is clear from the title, the BlocBuilder logic gets correctly executed only once, when the app is started. Afterwards, however, UI doesn't get rebuilt on state changes. Although all the events get emitted, and states change.
Thanks in advance for any help!
Here's my 'main.dart':
import 'package:co_flutter/auth/authentication_bloc.dart';
import 'package:co_flutter/auth/authentication_event.dart';
import 'package:co_flutter/auth/authentication_state.dart';
import 'package:co_flutter/auth/login/login_bloc.dart';
import 'package:co_flutter/auth/signup/signup_page.dart';
import 'package:co_flutter/loading_indicator.dart';
import 'package:co_flutter/splash_page.dart';
import 'package:co_flutter/user_repository.dart';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'auth/login/login_page.dart';
class SimpleBlocObserver extends BlocObserver {
#override
void onCreate(BlocBase bloc) {
super.onCreate(bloc);
print('onCreate -- ${bloc.runtimeType}');
}
#override
void onEvent(Bloc bloc, Object? event) {
super.onEvent(bloc, event);
print('onEvent -- ${bloc.runtimeType}, $event');
}
#override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print('onTransition -- ${bloc.runtimeType}, $transition');
}
#override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
print('onChange -- ${bloc.runtimeType}, $change');
}
#override
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
print('onError -- ${bloc.runtimeType}, $error');
super.onError(bloc, error, stackTrace);
}
#override
void onClose(BlocBase bloc) {
super.onClose(bloc);
print('onClose -- ${bloc.runtimeType}');
}
}
void main() {
Bloc.observer = SimpleBlocObserver();
runApp(MyApp(
userRepository: UserRepository(),
));
}
class MyApp extends StatefulWidget {
final UserRepository userRepository;
MyApp({Key? key, required this.userRepository}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late AuthenticationBloc authenticationBloc;
UserRepository get userRepository => widget.userRepository;
#override
void initState() {
authenticationBloc = AuthenticationBloc(userRepository: userRepository);
authenticationBloc.add(AppStarted());
super.initState();
}
#override
void dispose() {
authenticationBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AuthenticationBloc>(
create: (BuildContext context) => authenticationBloc,
),
BlocProvider<LoginBloc>(
create: (BuildContext context) => LoginBloc(
userRepository: userRepository,
authenticationBloc: authenticationBloc),
),
],
child: MaterialApp(
title: 'My App',
theme: ThemeData(
primarySwatch: Colors.indigo,
),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (BuildContext context, AuthenticationState state) {
if (state is AuthenticationUninitialized) {
return SplashPage();
}
if (state is AuthenticationAuthenticated) {
return Dashboard(
title: 'Dashboard',
);
}
if (state is AuthenticationUnauthenticated) {
return LoginPage(
userRepository: userRepository,
);
}
if (state is AuthenticationLoading) {
return LoadingIndicator();
}
// else {
// return Text('Error');
// }
return BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginToSignup) {
return SignUpPage();
} else
return SizedBox.shrink();
},
);
},
),
),
);
}
}
class Dashboard extends StatelessWidget {
final String title;
const Dashboard({Key? key, required this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
final AuthenticationBloc authenticationBloc =
BlocProvider.of<AuthenticationBloc>(context);
return Scaffold(
appBar: AppBar(
title: Text('Dashboard'),
),
body: Container(
child: Center(
child: ElevatedButton(
child: Text('logout'),
onPressed: () {
authenticationBloc.add(LoggedOut());
},
),
),
),
);
}
}
authentication_bloc
import 'dart:async';
import 'package:co_flutter/auth/authentication_event.dart';
import 'package:co_flutter/auth/authentication_state.dart';
import 'package:bloc/bloc.dart';
import '../user_repository.dart';
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
UserRepository userRepository;
AuthenticationBloc({required this.userRepository})
: super(AuthenticationUninitialized()) {
userRepository = UserRepository();
}
#override
Stream<AuthenticationState> mapEventToState(
AuthenticationEvent event,
) async* {
if (event is AppStarted) {
final bool hasToken = await userRepository.hasToken();
if (hasToken) {
yield AuthenticationAuthenticated();
} else {
yield AuthenticationUnauthenticated();
}
}
if (event is LoggedIn) {
yield AuthenticationLoading();
await userRepository.persistToken(event.token, event.userId);
yield AuthenticationAuthenticated();
}
if (event is LoggedOut) {
yield AuthenticationLoading();
await userRepository.deleteToken();
yield AuthenticationUnauthenticated();
}
}
}
authentication_event
import 'package:equatable/equatable.dart';
abstract class AuthenticationEvent extends Equatable {
#override
List<Object> get props => [];
}
class AppStarted extends AuthenticationEvent {
#override
String toString() => 'AppStarted';
}
class LoggedIn extends AuthenticationEvent {
final String token;
final String userId;
LoggedIn({required this.token, required this.userId});
#override
String toString() => 'LoggedIn { token: $token}';
}
class LoggedOut extends AuthenticationEvent {
#override
String toString() => 'LoggedOut';
}
authentication_state
import 'package:equatable/equatable.dart';
abstract class AuthenticationState extends Equatable {
const AuthenticationState();
}
class AuthenticationUninitialized extends AuthenticationState {
#override
List<Object> get props => [];
}
class AuthenticationLoading extends AuthenticationState {
#override
List<Object> get props => [];
}
class AuthenticationAuthenticated extends AuthenticationState {
#override
List<Object> get props => [];
}
class AuthenticationUnauthenticated extends AuthenticationState {
#override
List<Object> get props => [];
}
The problem is here. You already created a bloc:
authenticationBloc = AuthenticationBloc(userRepository: userRepository);
And here you are trying to create it again.
To fix it replace this code:
BlocProvider<AuthenticationBloc>(
create: (BuildContext context) => authenticationBloc,
),
with this:
BlocProvider<AuthenticationBloc>.value(
value: authenticationBloc,
),
Pass bloc into bloc builder
BlocBuilder<AuthenticationBloc, AuthenticationState>(
bloc: authenticationBloc,
Don't forget to dispose bloc authenticationBloc after you used it in dispose function.
in my app i want to detect in the splashscreen if this app is started for the first time.
For that i want to use the hive nosql package.
After that if the app is started for the first time it will open the welcome page and if not the login page.
main.dart
import 'package:flutter_config/flutter_config.dart';
import 'package:flutter/material.dart';
import 'package:app/pages/splash/splash_page.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'config/theme/theme.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterConfig.loadEnvVariables();
await Hive.initFlutter();
await Hive.openBox('settings');
runApp(const App());
}
class App extends StatefulWidget {
const App({Key? key}) : super(key: key);
#override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
#override
void dispose() async {
Hive.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'App',
debugShowCheckedModeBanner: false,
theme: lightThemeData(context),
darkTheme: darkThemeData(context),
home: const SplashPage(),
);
}
}
splash_page.dart
import 'package:flutter/material.dart';
import 'package:app/pages/login/login_page.dart';
import 'package:app/pages/welcome/welchome_page.dart';
import 'package:app/services/settings_service.dart';
class SplashPage extends StatefulWidget {
const SplashPage({Key? key}) : super(key: key);
#override
State<SplashPage> createState() => _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
#override
Widget build(BuildContext context) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (_) =>
Settings().isFirstTime ? const WelcomePage() : const LoginPage(),
),
);
return const Scaffold(
body: Center(
child: SizedBox(
width: 125,
height: 125,
child: Icon(Icons.clear),
),
),
);
}
}
there i call the function "var _isFirstTime = Settings().isFirstTime;" it should return me a bool
settings_service.dart
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
class Settings {
final Box _settingsStorage = Hive.box('settings');
get isFirstTime {
if (_settingsStorage.get('firstRun')) {
return true;
} else {
_settingsStorage.put('firstRun', true);
}
return false;
}
}
i got this error:
════════ Exception caught by widgets library ═══════════════════════════════════
The following _TypeError was thrown building SplashPage(dirty, state: _SplashPageState#261ae):
type 'Null' is not a subtype of type 'bool'
how can i solve this? later i would like to use the settings service for other settings as well ...
In the setting_service.dart,
I think this line -> _settingsStorage.get('firstRun'), is returning null. From what I understand what you should do is that whenever you get firstRun as null, you should assign it true.
if (_settingsStorage.get('firstRun') ?? true) {
return _settingsStorage.get('firstRun') ?? true;
}