簡體   English   中英

Flutter - 將 GetIt 與 BuildContext 結合使用

[英]Flutter - Using GetIt with BuildContext

我根據 flutter 文檔在我的應用程序中使用本地化。
看這里: https://flutter.dev/docs/development/accessibility-and-localization/internationalization

我使用 get_it package(版本 4.0.4)檢索 singleton 對象,如本地化委托。 不幸的是,它需要一個BuildContext屬性。 有時在我的應用程序中我沒有上下文引用,所以如果它像這樣工作會很好: GetIt.I<AppLocalizations>()而不是這個: AppLocalizations.of(context) 如果您像這樣設置 get_it,它仍然可以毫無問題地實現: GetIt.I.registerLazySingleton(() => AppLocalizations.of(context)); 問題是您至少需要一次上下文才能使其工作。 此外,如果您想在初始路由中立即顯示本地化文本,則很難在需要時立即獲得正確初始化的BuildContext

我很難正確解釋它,所以我在一個最小的例子中重新創建了這個問題。

我注釋掉了一些會導致編譯時錯誤的代碼,但它顯示了我想象它是如何完成的。

主要.dart

GetIt getIt = GetIt.instance;

void setupGetIt() {
  // How to get BuildContext properly if no context is available yet?
  // Compile time error.
  // getIt.registerLazySingleton(() => AppLocalizations.of(context));
}

void main() {
  setupGetIt();

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  MyApp({Key key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    // The above line also won't work. It has BuildContext but Applocalizations.of(context) won't work
    // because it's above in the Widget tree and not yet setted up.
    getIt.registerLazySingleton(() => AppLocalizations.of(context));
    return MaterialApp(
      supportedLocales: const [
        Locale('en', 'US'),
        Locale('hu', 'HU'),
      ],
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      localeResolutionCallback: (locale, supportedLocales) {
        // check if locale is supported
        for (final supportedLocale in supportedLocales) {
          if (supportedLocale.languageCode == locale?.languageCode &&
              supportedLocale.countryCode == locale?.countryCode) {
            return supportedLocale;
          }
        }
        // if locale is not supported then return the first (default) one
        return supportedLocales.first;
      },
      // You may pass the BuildContext here for Page1 in it's constructor 
      // but in a more advanced routing case it's not a maintanable solution.
      home: Page1(),
    );
  }
}

初始路線

class PageBase extends StatelessWidget {
  final String title;
  final Widget content;

  PageBase(this.title, this.content);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: content,
    );
  }
}

class Page1 extends PageBase {
  // It won't run because I need the context but clearly I don't have it.
  // And in a real app you also don't want to pass the context all over the place 
     if you have many routes to manage.
  Page1(String title)
      : super(AppLocalizations.of(context).title, Center(child: Text('Hello')));

  // Intended solution
  // I don't know how to properly initialize getIt AppLocalizations singleton by the time
  // it tries to retrieve it
  Page1.withGetIt(String title)
      : super(getIt<AppLocalizations>().title, Center(child: Text('Hello')));
}

locales.dart

String globalLocaleName;

class AppLocalizations {
  //AppLocalizations(this.localeName);

  static AppLocalizations of(BuildContext context) {
    return Localizations.of<AppLocalizations>(context, AppLocalizations);
  }

  static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationsDelegate();

  static Future<AppLocalizations> load(Locale locale) async {
    final String name =
        locale.countryCode.isEmpty ? locale.languageCode : locale.toString();

    final String localeName = Intl.canonicalizedLocale(name);

    return initializeMessages(localeName).then((_) {
      globalLocaleName = localeName;
      return AppLocalizations();
    });
  }

  String get title => Intl.message(
        'This is the title.',
        name: 'title',
      );
}

class _AppLocalizationsDelegate
    extends LocalizationsDelegate<AppLocalizations> {
  // This delegate instance will never change (it doesn't even have fields!)
  // It can provide a constant constructor.
  const _AppLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) {
    return ['en', 'hu'].contains(locale.languageCode);
  }

  @override
  Future<AppLocalizations> load(Locale locale) => AppLocalizations.load(locale);

  @override
  bool shouldReload(_AppLocalizationsDelegate old) => false;
}

而一些intl生成的dart代碼和不是那么重要的.arb文件說明問題。


總而言之,在這種情況下,我如何才能在不使用上下文的情況下將我的AppLocalizations class 用作 singleton? 也許我最初的方法不好,可以通過我所代表的其他方式來完成。 如果您有解決方案,請告訴我。

謝謝你。

要實現您所描述的內容,您需要首先使用get_it制作導航服務。 按照以下步驟實現結果:

1.創建導航服務

import 'package:flutter/material.dart';

class NavigationService {
  final GlobalKey<NavigatorState> navigatorKey =
      new GlobalKey<NavigatorState>();

  Future<dynamic> navigateTo(String routeName) {
    return navigatorKey.currentState!
        .push(routeName);
  }

  goBack() {
    return navigatorKey.currentState!.pop();
  }
}

這使您可以在整個應用程序中從任何位置導航到任何位置,而無需構建上下文。 您可以使用此導航器鍵來實現當前上下文的 AppLocalization 實例。 請參閱 FilledStacks 教程,了解這種在沒有構建上下文的情況下導航的方法。

https://www.filledstacks.com/post/navigate-without-build-context-in-flutter-using-a-navigation-service/

2.注冊

GetIt locator = GetIt.instance;

void setupLocator() {
  ...
  locator.registerLazySingleton(() => NavigationService());
  ...
}

3.在素材應用中分配導航鍵

return MaterialApp(
    ...
    navigatorKey: navigationService.navigatorKey,
    ...
  ),

3.為AppLocalizations創建一個實例並導入到任何你想使用的地方

localeInstance() => AppLocalizations.of(locator<NavigationService>().navigatorKey.currentContext!)!;

3.實際用例

import 'package:{your_app_name}/{location_to_this_instace}/{file_name}.dart';

localeInstance().your_localization_variable

您可以向 MaterialApp 添加一個構建器,並在其中使用可用的上下文設置服務定位器。 例子:

  Widget build(BuildContext context) {
    return MaterialApp(
          builder: (context, widget) {
            setUpServiceLocator(context);
            return FutureBuilder(
                future: getIt.allReady(),
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  if (snapshot.hasData) {
                    return widget;
                  } else {
                    return Container(color: Colors.white);
                  }
                });
          },
        );
}

服務定位器設置:

void setUpServiceLocator(BuildContext context) {
  getIt.registerSingleton<AppLocalizations>(AppLocalizations.of(context));
}

您可以將一些不可本地化的啟動畫面與FutureBuildergetIt.allReady()一起使用。

就像是:

  class SplashScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
      return FutureBuilder<void>(
        future: getIt.allReady(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            // Navigate to main page (with replace)
          } else if (snapshot.hasError) {
            // Error handling
          } else {
            // Some pretty loading indicator
          }
        },
      );
    }

我想推薦注射劑package 來處理 get_it。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM