[英]Flutter: Try to use BlocProvider with get_it and auto_route package but context don't find provider
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.我的 bloc 提供商有问题。 My application uses the auto_route package for routing, get_it for dependency injection and block to handle business logic/status.
我的应用程序使用 auto_route package 进行路由,使用 get_it 进行依赖注入,并使用块来处理业务逻辑/状态。
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.流程如下:我在异步 init() function 中初始化我的 get_it 实例的依赖项。在这些依赖项中有我的 AppRouter。
Once this is done, I runApp() my MyApp Widget which returns a MaterialApp.router() in which I delegate the routing to my AppRouter.完成后,我运行 App() 我的 MyApp Widget,它返回一个 MaterialApp.router(),我在其中将路由委托给我的 AppRouter。 The router is configured to have my LoginScreen as the initial page.
路由器配置为将我的 LoginScreen 作为初始页面。
My Login page is a Scaffold for a child BlocProvider.我的登录页面是子 BlocProvider 的脚手架。 This BlocProvider creates the bloc thanks to my locator service (get_it) and takes as child my body() widget.
由于我的定位器服务 (get_it),这个 BlocProvider 创建了 bloc,并将我的 body() 小部件作为孩子。
In body() I have a button that takes an onPressed function as parameter.在 body() 我有一个按钮,它接受一个 onPressed function 作为参数。 I give it my tryToLogin function as parameter, use the context to find my bloc and send the LoginWithPasswordAndUsername event.
我给它我的 tryToLogin function 作为参数,使用上下文找到我的集团并发送 LoginWithPasswordAndUsername 事件。
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.这是卡住的最后一步:当我单击我的按钮时,Flutter 告诉我它无法在 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主要.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 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路由器.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 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 login.dart问题 function 就在这个低端
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 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
════════════════════════════════════════════════════════════════════════════════════════════════════
Maybe you are missing a builder?也许你错过了一个建设者?
Try the code below.试试下面的代码。
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => getIt<AuthBloc>(),
child: Builder(
builder: (context) => body(context),
),
),
);
}
You provide the bloc, but not the BlocBuilder.您提供 bloc,但不提供 BlocBuilder。 See below (I combined the
build
and body
methods, since build()
just called body()
).见下文(我结合了
build
和body
方法,因为build()
刚刚称为body()
)。
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => getIt<AuthBloc>(),
child: BlocBuilder<AuthBloc, AuthState>(
builder: (context, state) {
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();
})
],
),
))
],
),
),
);
}
Use implements AutoRouteWrapper from Auto Route Package使用自动路由 Package 中的工具 AutoRouteWrapper
class LoginScreen extends StatefulWidget implements AutoRouteWrapper {
@override
_LoginScreenState createState() =>_LoginScreenState();
@override
Widget wrappedRoute(BuildContext context) {
return BlocProvider(create: (ctx)=> getIt<AuthBloc>(), child: this);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.