使用 get_it 的 Flutter 依赖注入设置问题
Posted
技术标签:
【中文标题】使用 get_it 的 Flutter 依赖注入设置问题【英文标题】:Flutter dependency injection set-up problem with get_it 【发布时间】:2020-10-25 10:51:38 【问题描述】:我正在尝试学习如何在 Flutter 项目中设置依赖注入,但遇到了一个问题:
I/flutter (14507): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (14507): The following assertion was thrown building _InheritedProviderScope<NumberTriviaBloc>(value: null):
I/flutter (14507): No type GetConcreteNumberTrivia is registered inside GetIt.
I/flutter (14507): Did you forget to pass an instance name?
I/flutter (14507): (Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;did you
I/flutter (14507): forget to register it?)
I/flutter (14507): 'package:get_it/get_it_impl.dart':
I/flutter (14507): Failed assertion: line 251 pos 14: 'instanceFactory != null'
I/flutter (14507):
I/flutter (14507): The relevant error-causing widget was:
I/flutter (14507): _InheritedProviderScope<NumberTriviaBloc>
依赖初始化:
final sl = GetIt.instance;
void init()
//! Features - Number Trivia
//* Bloc
sl.registerFactory<NumberTriviaBloc>(() => NumberTriviaBloc(concrete: sl(), inputConverter: sl(), random: sl()));
//* Use cases
sl.registerLazySingleton<UseCase<NumberTrivia, Params>>(() => GetConcreteNumberTrivia(sl()));
sl.registerLazySingleton<UseCase<NumberTrivia, NoParams>>(() => GetRandomNumberTrivia(sl()));
//* Repository
sl.registerLazySingleton<NumberTriviaRepository>(() => NumberTriviaRepositoryImplementation(
localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));
//* Data sources
sl.registerLazySingleton<NumberTriviaRemoteDataSource>(
() => NumberTriviaRemoteDataSourceImpl(client: sl()));
sl.registerLazySingleton<NumberTriviaLocalDataSource>(
() => NumberTriviaLocalDataSourceImpl(sharedPreferences: sl()));
//! Core
sl.registerLazySingleton<InputConverter>(() => InputConverter());
sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));
//! External
sl.registerLazySingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance());
sl.registerLazySingleton<http.Client>(() => http.Client());
sl.registerLazySingleton<DataConnectionChecker>(() => DataConnectionChecker());
我不明白是什么问题,这里是其他东西初始化的代码:
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState>
final GetConcreteNumberTrivia getConcreteNumberTrivia;
final GetRandomNumberTrivia getRandomNumberTrivia;
final InputConverter inputConverter;
NumberTriviaBloc(
@required GetConcreteNumberTrivia concrete,
@required GetRandomNumberTrivia random,
@required this.inputConverter)
: assert(concrete != null),
assert(random != null),
assert(inputConverter != null),
getConcreteNumberTrivia = concrete,
getRandomNumberTrivia = random;
...
class GetConcreteNumberTrivia implements UseCase<NumberTrivia, Params>
final NumberTriviaRepository repository;
GetConcreteNumberTrivia(this.repository);
@override
Future<Either<Failure, NumberTrivia>> call(Params params) async
return await repository.getConcreteNumberTrivia(params.number);
class GetRandomNumberTrivia implements UseCase<NumberTrivia, NoParams>
final NumberTriviaRepository repository;
GetRandomNumberTrivia(this.repository);
@override
Future<Either<Failure, NumberTrivia>> call(NoParams params) async
return await repository.getRandomNumberTrivia();
class NumberTriviaRepositoryImplementation implements NumberTriviaRepository
final NumberTriviaRemoteDataSource remoteDataSource;
final NumberTriviaLocalDataSource localDataSource;
final NetworkInfo networkInfo;
NumberTriviaRepositoryImplementation(
@required this.remoteDataSource,
@required this.localDataSource,
@required this.networkInfo);
...
class NumberTriviaRemoteDataSourceImpl implements NumberTriviaRemoteDataSource
final http.Client client;
NumberTriviaRemoteDataSourceImpl(@required this.client);
...
class NumberTriviaLocalDataSourceImpl implements NumberTriviaLocalDataSource
final SharedPreferences sharedPreferences;
NumberTriviaLocalDataSourceImpl(@required this.sharedPreferences);
...
class InputConverter
class NetworkInfoImpl extends NetworkInfo
final DataConnectionChecker connectionChecker;
NetworkInfoImpl(this.connectionChecker);
...
小部件是:
void main()
di.init();
runApp(MyApp());
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primaryColor: Colors.green.shade800,
accentColor: Colors.green.shade600,
),
home: NumberTriviaPage(),
);
class NumberTriviaPage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text('Number Trivia'),
),
body: SingleChildScrollView(child: buildBody(context)),
);
BlocProvider<NumberTriviaBloc> buildBody(BuildContext context)
return BlocProvider<NumberTriviaBloc>(
create: (_) => sl<NumberTriviaBloc>(),
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: <Widget>[
SizedBox(
height: 10,
),
BlocBuilder<NumberTriviaBloc, NumberTriviaState>(
// ignore: missing_return
builder: (context, state)
if (state is Empty)
return MessageDisplay(
message: 'Start searching',
);
else if (state is Loading)
return LoadingWidget();
else if (state is Loaded)
return TriviaDisplay(
numberTrivia: state.trivia,
);
else if (state is Error)
return MessageDisplay(
message: state.message,
);
,
),
SizedBox(
height: 20,
),
TriviaControl()
],
),
),
),
);
我不明白为什么这对我不起作用。 我按照 youtube 上的教程进行操作,并在 github 存储库上检查了本教程中的代码,但这对我没有帮助。 那你能给我解释一下是怎么回事吗?
在 tnc1997 答案的帮助下,我遇到了一个新错误:
lib/injection_container.dart:27:91: Error: The argument type 'NumberTriviaRepository/*1*/' can't be assigned to the parameter type 'NumberTriviaRepository/*2*/'.
- 'NumberTriviaRepository/*1*/' is from 'package:clean_architecture/features/number_trivia/domain/repositories/number_trivia_repository.dart' ('lib/features/number_trivia/domain/repositories/number_trivia_repository.dart').
- 'NumberTriviaRepository/*2*/' is from 'lib/features/number_trivia/domain/repositories/number_trivia_repository.dart'.
sl.registerLazySingleton<UseCase<NumberTrivia, NoParams>>(() => GetRandomNumberTrivia(sl<NumberTriviaRepository>()));
这是同一个文件,下面一行使用了类似的合约
//* Use cases
sl.registerLazySingleton<UseCase<NumberTrivia, Params>>(() => GetConcreteNumberTrivia(sl<NumberTriviaRepository>()));
sl.registerLazySingleton<UseCase<NumberTrivia, NoParams>>(() => GetRandomNumberTrivia(sl<NumberTriviaRepository>()));
//* Repository
sl.registerLazySingleton<NumberTriviaRepository>(() => NumberTriviaRepositoryImplementation(
localDataSource: sl<NumberTriviaLocalDataSource>(), networkInfo: sl<NetworkInfo>(), remoteDataSource: sl<NumberTriviaRemoteDataSource>()));
现在我比以前更困惑了
【问题讨论】:
是否有机会添加显示NumberTriviaPage
的代码示例以及显示initialise 依赖关系的代码示例?
能分享一下di.init()
的实现吗?
不是您问题的答案,但仍可能对您有用。看看可注射的。它是一个库,它将使用代码生成器根据您放在类上的注释为您创建 get_it 依赖注入设置。 pub.dev/packages/injectable
【参考方案1】:
我很确定在使用 GetIt
包时,您需要依赖接口而不是构造函数中的具体类型。毕竟,依赖注入的原则之一是依赖接口而不是实现,从而允许您轻松切换实现。我在下面添加了一个代码示例,展示了如何做到这一点。如果您还没有这样做,您可能需要查看文档here 以获取更多信息和代码示例。如果您对代码示例有任何问题,请随时发表评论!
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState>
final UseCase<NumberTrivia, Params> getConcreteNumberTrivia;
final UseCase<NumberTrivia, NoParams> getRandomNumberTrivia;
final InputConverter inputConverter;
NumberTriviaBloc(
@required UseCase<NumberTrivia, Params> concrete,
@required UseCase<NumberTrivia, NoParams> random,
@required this.inputConverter)
: assert(concrete != null),
assert(random != null),
assert(inputConverter != null),
getConcreteNumberTrivia = concrete,
getRandomNumberTrivia = random;
...
sl.registerFactory<NumberTriviaBloc>(() => NumberTriviaBloc(concrete: sl<UseCase<NumberTrivia, Params>>(), inputConverter: sl<InputConverter>(), random: sl<UseCase<NumberTrivia, NoParams>>()));
【讨论】:
谢谢你的回复,但我遇到了一个新错误,我编辑了我的帖子,现在它包含了它,你能检查一下吗? 这可能是不同导入语句格式的问题。您能否仔细检查您的所有导入语句是否使用相同的格式,并且您没有一个像这样package:clean_architecture/features/...
写的导入语句和另一个像这样写lib/features/...
的导入语句?以上是关于使用 get_it 的 Flutter 依赖注入设置问题的主要内容,如果未能解决你的问题,请参考以下文章
Flutter get_it 和 FutureBuilder 无法使用热重载