I try to make a login page for my app who permit to access when password and username are okay.
I have some problem with my bloc provider. My application uses the auto_route package for routing, get_it for dependency injection and block to handle business logic/status.
The flow is as follows: I initialize the dependencies of my get_it instance in an asynchronous init() function. In these dependencies is among others my AppRouter.
Once this is done, I runApp() my MyApp Widget which returns a MaterialApp.router() in which I delegate the routing to my AppRouter. The router is configured to have my LoginScreen as the initial page.
My Login page is a Scaffold for a child BlocProvider. This BlocProvider creates the bloc thanks to my locator service (get_it) and takes as child my body() widget.
In body() I have a button that takes an onPressed function as parameter. I give it my tryToLogin function as parameter, use the context to find my bloc and send the LoginWithPasswordAndUsername event.
It's this last step that is stuck: when I click on my button, Flutter tells me that it can't find my Provider in the BuildContext. I don't understand where this can come from, is it my router that is at fault? or another element that I didn't see? The logic seems good to me and I really can't find it. Could you please help me?
You can find the code below.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:lansknet/presentation/pages/Login.dart';
import 'package:lansknet/router/router.dart';
import 'package:lansknet/injection_container.dart' as di;
void main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
await di.init().then((_) => FlutterNativeSplash.remove());
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({
Key? key,
}) : super(key: key);
final _appRouter = di.getIt<AppRouter>();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerDelegate: _appRouter.delegate(),
routeInformationParser: _appRouter.defaultRouteParser(),
title: "Lansknet",
debugShowCheckedModeBanner: false,
theme: ThemeData(),
darkTheme: ThemeData.dark(),
);
}
}
injection_container.dart
import 'package:get_it/get_it.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:lansknet/core/network_info.dart';
import 'package:lansknet/data/datasources/auth_local_data_source.dart';
import 'package:lansknet/data/datasources/auth_remote_data_source.dart';
import 'package:lansknet/data/repositories/auth_repository_impl.dart';
import 'package:lansknet/domain/repositories/AuthRepository.dart';
import 'package:lansknet/domain/usecases/Login.dart';
import 'package:lansknet/presentation/blocs/bloc/auth_bloc.dart';
import 'package:lansknet/presentation/utils/input_converter.dart';
import 'package:lansknet/router/router.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
final getIt = GetIt.instance;
Future<void> init() async {
//! Features - Auth
// Bloc
getIt.registerFactory(
() => AuthBloc(login: getIt()),
);
// Use cases
getIt.registerLazySingleton(() => Login(getIt()));
// Repository
getIt.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(
localDataSource: getIt(),
networkInfo: getIt(),
remoteDataSource: getIt(),
),
);
// Data sources
getIt.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(client: getIt()),
);
getIt.registerLazySingleton<AuthLocalDataSource>(
() => AuthLocalDataSourceImpl(sharedPreferences: getIt()),
);
//! Core
getIt.registerLazySingleton(() => InputConverter());
getIt.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(getIt()));
//! External
final sharedPreferences = await SharedPreferences.getInstance();
getIt.registerLazySingleton(() => sharedPreferences);
getIt.registerLazySingleton(() => http.Client());
getIt.registerLazySingleton(() => InternetConnectionChecker());
//! Routes
getIt.registerSingleton<AppRouter>(AppRouter());
}
router.dart
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:lansknet/presentation/pages/home.dart';
import 'package:lansknet/presentation/pages/login.dart';
part 'router.gr.dart';
@MaterialAutoRouter(replaceInRouteName: 'Screen,Route', routes: <AutoRoute>[
AutoRoute(page: LoginScreen, initial: true, path: '/login'),
AutoRoute(page: HomeScreen, path: '/home')
])
class AppRouter extends _$AppRouter {}
router.gr.dart
// **************************************************************************
// AutoRouteGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// AutoRouteGenerator
// **************************************************************************
//
// ignore_for_file: type=lint
part of 'router.dart';
class _$AppRouter extends RootStackRouter {
_$AppRouter([GlobalKey<NavigatorState>? navigatorKey]) : super(navigatorKey);
@override
final Map<String, PageFactory> pagesMap = {
LoginRoute.name: (routeData) {
return MaterialPageX<dynamic>(routeData: routeData, child: LoginScreen());
},
HomeRoute.name: (routeData) {
return MaterialPageX<dynamic>(
routeData: routeData, child: const HomeScreen());
}
};
@override
List<RouteConfig> get routes => [
RouteConfig('/#redirect',
path: '/', redirectTo: '/login', fullMatch: true),
RouteConfig(LoginRoute.name, path: '/login'),
RouteConfig(HomeRoute.name, path: '/home')
];
}
/// generated route for
/// [LoginScreen]
class LoginRoute extends PageRouteInfo<void> {
const LoginRoute() : super(LoginRoute.name, path: '/login');
static const String name = 'LoginRoute';
}
/// generated route for
/// [HomeScreen]
class HomeRoute extends PageRouteInfo<void> {
const HomeRoute() : super(HomeRoute.name, path: '/home');
static const String name = 'HomeRoute';
}
login.dart problem function is on the low of this
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lansknet/injection_container.dart';
import 'package:lansknet/presentation/blocs/bloc/auth_bloc.dart';
import 'package:lansknet/presentation/widgets/call_to_action_button.dart';
import 'package:lansknet/presentation/widgets/large_button.dart';
import 'package:lansknet/presentation/widgets/loading_circle.dart';
import 'package:lansknet/presentation/widgets/login_field.dart';
import 'package:lansknet/presentation/widgets/login_input_text_field.dart';
import 'package:lansknet/router/router.dart';
import 'package:provider/provider.dart';
import 'package:lansknet/injection_container.dart';
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final TextEditingController emailField = TextEditingController();
final TextEditingController passwordField = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => getIt<AuthBloc>(),
child: body(context),
),
);
}
Widget body(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: GestureDetector(
child: Stack(
children: <Widget>[
Container(
height: double.infinity,
width: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color.fromRGBO(0, 255, 255, 1),
Color.fromRGBO(0, 200, 205, 1),
Color.fromRGBO(0, 150, 155, 1),
Color.fromRGBO(0, 120, 125, 1),
]),
),
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
padding:
const EdgeInsets.symmetric(horizontal: 25, vertical: 120),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Align(
alignment: Alignment.topLeft,
child: Text(
"Sign In",
style: TextStyle(
color: Colors.white,
fontSize: 40,
fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 50),
LoginField(
emailField: emailField, passwordField: passwordField),
const CallToActionButton(text: 'Forgot password'),
//buildRemeberCb(),
LargeButton(
name: "Login",
onPressed: () =>
{tryToLogin(emailField, passwordField)},
color: const Color.fromRGBO(0, 200, 205, 1)),
BlocBuilder<AuthBloc, AuthState>(
builder: (context, state) {
if (state is Loading) {
return const LoadingCircle();
} else if (state is Loaded) {
AutoRouter.of(context)
.replaceAll([const HomeRoute()]);
} else if (state is Error) {
return SnackBar(
backgroundColor: Colors.redAccent,
content: Text(state.message),
);
}
return Container();
})
],
),
))
],
),
),
);
}
void tryToLogin(TextEditingController email, TextEditingController password) {
context
.read<AuthBloc>()
.add(LoginWithPasswordAndUsername(email.text, password.text));
}
}
Output
══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following ProviderNotFoundException was thrown while handling a gesture:
Error: Could not find the correct Provider<AuthBloc> above this LoginScreen Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that LoginScreen is under your MultiProvider/Provider<AuthBloc>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using `builder` like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:356:7)
#1 Provider.of (package:provider/src/provider.dart:293:30)
#2 ReadContext.read (package:provider/src/provider.dart:656:21)
#3 _LoginScreenState.tryToLogin (package:lansknet/presentation/pages/login.dart:109:10)
#4 _LoginScreenState.body.<anonymous closure> (package:lansknet/presentation/pages/login.dart:81:32)
#5 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:1005:21)
#6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:198:24)
#7 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:613:11)
#8 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:298:5)
#9 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:269:7)
#10 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:157:27)
#11 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:449:20)
#12 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:425:22)
#13 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:329:11)
#14 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:380:7)
#15 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:344:5)
#16 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:302:7)
#17 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:285:7)
#21 _invoke1 (dart:ui/hooks.dart:170:10)
#22 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:331:7)
#23 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
(elided 3 frames from dart:async)
Handler: "onTap"
Recognizer:
TapGestureRecognizer#b548c
════════════════════════════════════════════════════════════════════════════════════════════════════