使用 get_it 的 Flutter 依赖注入(inject)设置问题

标签 flutter dependency-injection

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

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>()));
现在我比以前更困惑了

最佳答案

我很确定在使用 GetIt 时你需要依赖接口(interface)而不是构造函数中的具体类型。毕竟,依赖注入(inject)的原则之一是依赖接口(interface)而不是实现,从而允许您轻松切换实现。我在下面添加了一个代码示例,展示了如何做到这一点。您可能想查看文档 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>>()));

关于使用 get_it 的 Flutter 依赖注入(inject)设置问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62736998/

相关文章:

flutter - flutter 缓存网页中的 webview 是否会发生?

dart - flutter 中的嵌套 ListViews 给出水平视口(viewport)错误

dart - Flutter:从子小部件设置父小部件状态

java - 了解Java的注入(inject)框架HK2吗?

java - 使用 spring jee-jndi 查找注入(inject)的 JMS XA 连接工厂的行为与 Weblogic 12c 上的 @Resource 注入(inject)不同

java - Linux - 默认 java 版本为 8,Flutter 设置为 11

android - 编程新手 - 在指定位置找不到 Flutter SDK

.net - 关于依赖注入(inject)和工厂的疑惑

Spring 框架 - 注入(inject) bean 子集

java - 无法让 Spring 依赖注入(inject)工作