Flutter 的 get_it 依赖注入设置问题

Ger*_*pea 9 dependency-injection flutter

我正在尝试学习如何在 flutter 项目中设置依赖项注入,但我遇到了一个问题:

\n
I/flutter (14507): \xe2\x95\x90\xe2\x95\x90\xe2\x95\xa1 EXCEPTION CAUGHT BY WIDGETS LIBRARY \xe2\x95\x9e\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\nI/flutter (14507): The following assertion was thrown building _InheritedProviderScope<NumberTriviaBloc>(value: null):\nI/flutter (14507): No type GetConcreteNumberTrivia is registered inside GetIt.\nI/flutter (14507):  Did you forget to pass an instance name?\nI/flutter (14507): (Did you accidentally do  GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;did you\nI/flutter (14507): forget to register it?)\nI/flutter (14507): 'package:get_it/get_it_impl.dart':\nI/flutter (14507): Failed assertion: line 251 pos 14: 'instanceFactory != null'\nI/flutter (14507): \nI/flutter (14507): The relevant error-causing widget was:\nI/flutter (14507):   _InheritedProviderScope<NumberTriviaBloc>\n
Run Code Online (Sandbox Code Playgroud)\n

依赖初始化:

\n
final sl = GetIt.instance;\n\nvoid init() {\n  //! Features - Number Trivia\n  //* Bloc\n  sl.registerFactory<NumberTriviaBloc>(() => NumberTriviaBloc(concrete: sl(), inputConverter: sl(), random: sl()));\n\n  //* Use cases\n  sl.registerLazySingleton<UseCase<NumberTrivia, Params>>(() => GetConcreteNumberTrivia(sl()));\n  sl.registerLazySingleton<UseCase<NumberTrivia, NoParams>>(() => GetRandomNumberTrivia(sl()));\n\n  //* Repository\n  sl.registerLazySingleton<NumberTriviaRepository>(() => NumberTriviaRepositoryImplementation(\n      localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));\n\n  //* Data sources\n  sl.registerLazySingleton<NumberTriviaRemoteDataSource>(\n      () => NumberTriviaRemoteDataSourceImpl(client: sl()));\n\n  sl.registerLazySingleton<NumberTriviaLocalDataSource>(\n      () => NumberTriviaLocalDataSourceImpl(sharedPreferences: sl()));\n\n  //! Core\n  sl.registerLazySingleton<InputConverter>(() => InputConverter());\n  sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));\n\n  //! External\n  sl.registerLazySingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance());\n  sl.registerLazySingleton<http.Client>(() => http.Client());\n  sl.registerLazySingleton<DataConnectionChecker>(() => DataConnectionChecker());\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我不明白问题是什么,这是其他东西初始化的代码:

\n
class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {\n  final GetConcreteNumberTrivia getConcreteNumberTrivia;\n  final GetRandomNumberTrivia getRandomNumberTrivia;\n  final InputConverter inputConverter;\n\n  NumberTriviaBloc(\n      {@required GetConcreteNumberTrivia concrete,\n      @required GetRandomNumberTrivia random,\n      @required this.inputConverter})\n      : assert(concrete != null),\n        assert(random != null),\n        assert(inputConverter != null),\n        getConcreteNumberTrivia = concrete,\n        getRandomNumberTrivia = random;\n  ...\n}\n\nclass GetConcreteNumberTrivia implements UseCase<NumberTrivia, Params>{\n  final NumberTriviaRepository repository;\n\n  GetConcreteNumberTrivia(this.repository);\n\n  @override\n  Future<Either<Failure, NumberTrivia>> call(Params params) async {\n    return await repository.getConcreteNumberTrivia(params.number);\n  }\n}\n\nclass GetRandomNumberTrivia implements UseCase<NumberTrivia, NoParams> {\n  final NumberTriviaRepository repository;\n\n  GetRandomNumberTrivia(this.repository);\n\n  @override\n  Future<Either<Failure, NumberTrivia>> call(NoParams params) async {\n    return await repository.getRandomNumberTrivia();\n  }\n}\n\nclass NumberTriviaRepositoryImplementation implements NumberTriviaRepository {\n  final NumberTriviaRemoteDataSource remoteDataSource;\n  final NumberTriviaLocalDataSource localDataSource;\n  final NetworkInfo networkInfo;\n\n  NumberTriviaRepositoryImplementation(\n      {@required this.remoteDataSource,\n      @required this.localDataSource,\n      @required this.networkInfo});\n  ...\n}\n\nclass NumberTriviaRemoteDataSourceImpl implements NumberTriviaRemoteDataSource {\n  final http.Client client;\n\n  NumberTriviaRemoteDataSourceImpl({@required this.client});\n  ...\n}\n\nclass NumberTriviaLocalDataSourceImpl implements NumberTriviaLocalDataSource {\n  final SharedPreferences sharedPreferences;\n\n  NumberTriviaLocalDataSourceImpl({@required this.sharedPreferences});\n  ...\n}\n\nclass InputConverter {}\n\nclass NetworkInfoImpl extends NetworkInfo {\n  final DataConnectionChecker connectionChecker;\n\n  NetworkInfoImpl(this.connectionChecker);\n  ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n

小部件是:

\n
void main() {\n  di.init();\n  runApp(MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Flutter Demo',\n      theme: ThemeData(\n        primaryColor: Colors.green.shade800,\n        accentColor: Colors.green.shade600,\n      ),\n      home: NumberTriviaPage(),\n    );\n  }\n}\n\nclass NumberTriviaPage extends StatelessWidget {\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      appBar: AppBar(\n        title: Text('Number Trivia'),\n      ),\n      body: SingleChildScrollView(child: buildBody(context)),\n    );\n  }\n\n  BlocProvider<NumberTriviaBloc> buildBody(BuildContext context) {\n    return BlocProvider<NumberTriviaBloc>(\n      create: (_) => sl<NumberTriviaBloc>(),\n      child: Center(\n        child: Padding(\n          padding: const EdgeInsets.all(10),\n          child: Column(\n            children: <Widget>[\n              SizedBox(\n                height: 10,\n              ),\n              BlocBuilder<NumberTriviaBloc, NumberTriviaState>(\n                // ignore: missing_return\n                builder: (context, state) {\n                  if (state is Empty) {\n                    return MessageDisplay(\n                      message: 'Start searching',\n                    );\n                  } else if (state is Loading) {\n                    return LoadingWidget();\n                  } else if (state is Loaded) {\n                    return TriviaDisplay(\n                      numberTrivia: state.trivia,\n                    );\n                  } else if (state is Error) {\n                    return MessageDisplay(\n                      message: state.message,\n                    );\n                  }\n                },\n              ),\n              SizedBox(\n                height: 20,\n              ),\n              TriviaControl()\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我不明白为什么这对我不起作用。\n我按照 youtube 上的教程进行操作,还检查了 github 存储库上本教程中的代码,但这对我没有帮助。\n那么你能向我解释一下发生了什么吗?

\n

在 tnc1997 答案的帮助下,我收到了一个新错误:

\n
lib/injection_container.dart:27:91: Error: The argument type 'NumberTriviaRepository/*1*/' can't be assigned to the parameter type 'NumberTriviaRepository/*2*/'.\n - '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').\n - 'NumberTriviaRepository/*2*/' is from 'lib/features/number_trivia/domain/repositories/number_trivia_repository.dart'.\n  sl.registerLazySingleton<UseCase<NumberTrivia, NoParams>>(() => GetRandomNumberTrivia(sl<NumberTriviaRepository>()));\n
Run Code Online (Sandbox Code Playgroud)\n

这是同一个文件,下面一行使用类似的合同

\n
//* Use cases\n  sl.registerLazySingleton<UseCase<NumberTrivia, Params>>(() => GetConcreteNumberTrivia(sl<NumberTriviaRepository>()));\n  sl.registerLazySingleton<UseCase<NumberTrivia, NoParams>>(() => GetRandomNumberTrivia(sl<NumberTriviaRepository>()));\n\n  //* Repository\n  sl.registerLazySingleton<NumberTriviaRepository>(() => NumberTriviaRepositoryImplementation(\n      localDataSource: sl<NumberTriviaLocalDataSource>(), networkInfo: sl<NetworkInfo>(), remoteDataSource: sl<NumberTriviaRemoteDataSource>()));\n
Run Code Online (Sandbox Code Playgroud)\n

现在我比以前更困惑了

\n

tnc*_*997 3

我非常确定,在使用该GetIt包时,您需要依赖于接口,而不是构造函数中的具体类型。毕竟,依赖注入的原则之一是依赖接口而不是实现,从而允许您轻松切换实现。我在下面添加了一个代码示例,展示了如何完成此操作。如果您还没有这样做,您可能需要查看此处的文档以获取更多信息和代码示例。如果您对代码示例有任何问题,请随时发表评论!

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;
  ...
}
Run Code Online (Sandbox Code Playgroud)
sl.registerFactory<NumberTriviaBloc>(() => NumberTriviaBloc(concrete: sl<UseCase<NumberTrivia, Params>>(), inputConverter: sl<InputConverter>(), random: sl<UseCase<NumberTrivia, NoParams>>()));
Run Code Online (Sandbox Code Playgroud)