Searles
Searles

Reputation: 1837

Flutter returns null for AppLocalization.of(context)

I am using Flutter to build a Web-App and I want to use the internationalization feature of flutter on my new app. I was following the Flutter-Tutorial and I try to set the app-title using the arb-file. As mentioned in the tutorial, the app_localization.dart-files are created properly for 'en' and 'de'. Yet, I get a null pointer exception in the following code.

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MyApp',
      localizationsDelegates: [
        AppLocalizations.delegate, // Post-EDIT due to croxx5f
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('de', ''),
        Locale('en', ''),
      ],
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text(AppLocalizations.of(context)!.appTitle),
        ),
        body: Text(AppLocalizations.of(context)!.appTitle)
      ),
    );
  }
}

In fact, AppLocalizations.of(context) returns null.

Upvotes: 28

Views: 14502

Answers (9)

Jose Juan Calvo
Jose Juan Calvo

Reputation: 106

In version 0.19.0 the AppLocalizations class also provides auto-generated localizationsDelegates and supportedLocales lists.

Here it is a complete exemple.

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      locale: Locale('es', 'ES'),
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.helloWorld),
      ),
      body: Center(
        child: Text(AppLocalizations.of(context)!.helloWorld),
      ),
    );
  }
}

Remember add

flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl

You may need to modify the pubspec.yaml file and enable the generate flag. At the end of the file add this line:

flutter:
  generate: true # Add this line

Finally create into de lib folder a subfolder called l10n and create a localization file with name intl_en.arb o intl_es.arb with this content:

In Spanish

{
    "@@locale": "es",
    "helloWorld": "Hola Mundo",
    "@helloWorld": {
        "description": "El tradicional mensaje de Hola Mundo"
    }
}

In English

{
    "@@locale": "en",
    "helloWorld": "Hello World",
    "@helloWorld": {
        "description": "The traditional Hello World message"
    }
}  

Add configuration file called l10n.yaml in root directory near pubspec.yaml and fill with

arb-dir: lib/l10n
template-arb-file: intl_en.arb
output-localization-file: app_localizations.dart
untranslated-messages-file: desiredFileName.txt

Finally run

flutter pub get

and run the application.

That's all

Upvotes: 0

Cong Dan Luong
Cong Dan Luong

Reputation: 1702

I have a trick solution: delay a bit for the AppLocalizations to be loaded

Future<bool> getDelay() async {
    await Future.delayed(const Duration(milliseconds: 100));
    // fix AppLocalizations.of(context) return null
    // delay a bit for the AppLocalizations to be loaded
    return true;
}

Then, use it in builder

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: Locale(widget.defaultLanguage),
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      home: Scaffold(
        extendBodyBehindAppBar: false,
        backgroundColor: Colors.red,
        appBar: AppBar(
            backgroundColor: Colors.yellow,
            elevation: 0,
            // >>>>>> Solution here <<<<<<
            title: FutureBuilder(
              future: getDelay(),
              builder: (context, snapshot) {
                if (snapshot.connectionState != ConnectionState.done) {
                  return const Text("Loading...");
                }
                final _ = AppLocalizations.of(context);
                return Text(_?.app_title ?? "NULL TITLE");
              },
            )),
        body: ...
     ),
   );
 }

Upvotes: 0

Sriganesh
Sriganesh

Reputation: 31

In my case, I added AppLocalizations in MaterialApp of my main dart file. The localisation became null if I called MaterialApp again on any other page.

    return MaterialApp(
        debugShowCheckedModeBanner: false,
        locale: const Locale('ja'),
        supportedLocales: L10n.all,
        localizationsDelegates: [
          AppLocalizations.delegate,
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
          GlobalCupertinoLocalizations.delegate,
        ],

So, I had to find MaterialApp everywhere except the main dart file and comment out those lines, like below.


    return 
    // MaterialApp(
    //     debugShowCheckedModeBanner: false,
    //     home: 
        Scaffold(
           ...
        )
    // )
    ;

Upvotes: 1

DarkNeuron
DarkNeuron

Reputation: 8701

Irrelevant to what the op was asking for, but:

If you find yourself needing to set a localized title like so:

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context);
    final title = l10n.myTitle;

    return MaterialApp(
      title: title,
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      ...
    );
  }

That won't work, as you're trying to find an instance of AppLocalization that isn't included yet.

However, you can use the onGenerateTitle callback, which includes a context that CAN find AppLocalization.

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateTitle: (context) {
        final l10n = AppLocalizations.of(context);

        return l10n.myTitle;
      },
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      ...
    );
  }

Upvotes: 6

MillCreeker
MillCreeker

Reputation: 49

It's possible that you followed a tutorial/course like the one from Fireship (I had the same problem because of this). There, he used a FutureBuilder where he builds another MaterialApp. You'd need to assign the same localization parameters as in the first one. Otherwise, the context object doesn't know about the AppLocalization object.

Upvotes: 0

ALNAJJAR
ALNAJJAR

Reputation: 483

add in l10n.yaml:

nullable-getter: false

Upvotes: 4

Rajesh
Rajesh

Reputation: 4007

On my case, using the Scaffold() as home: of MaterialApp() for testing purpose caused the issue.

Once I created a Separate HomePage() widget and used that as home: HomePage() the problem is gone.

When I put the Scaffold(), as a direct child to MaterialApp, that treats as there were no parent contexts available with AppLocalizationDelegate, as the MaterialApp is the main context builder for all the underlying widgets.

Bad code:

return MaterialApp(
                home: Scaffold(
                  body: Txt(text: AppLocalizations.of(context)?.helloWorld)
                ),
                locale: Locale('ar'),
                supportedLocales: [Locale('en'),Locale('ar')],
                localizationsDelegates: [
                  AppLocalizations.delegate,
                  GlobalMaterialLocalizations.delegate,
                  GlobalCupertinoLocalizations.delegate,
                  GlobalWidgetsLocalizations.delegate,
                ]);
          },
        ));

Good code:

return MaterialApp(
                home: HomePag(),
                locale: Locale('ar'),
                supportedLocales: [Locale('en'),Locale('ar')],
                localizationsDelegates: [
                  AppLocalizations.delegate,
                  GlobalMaterialLocalizations.delegate,
                  GlobalCupertinoLocalizations.delegate,
                  GlobalWidgetsLocalizations.delegate,
                ]);
          },
        ));



class HomePag extends StatefulWidget {
  const HomePag({Key? key}) : super(key: key);

  @override
  State<HomePag> createState() => _HomePagState();
}

class _HomePagState extends State<HomePag> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Txt(text: AppLocalizations.of(context)?.helloWorld),
            // Txt(text: AppLocalizations.of(context)?.translate('help'))
          ],
        ),
      ),
    );
  }
}

Upvotes: 25

Aiiboo
Aiiboo

Reputation: 723

Thats helped me. I had a CupertinoApp above MaterielApp, this override the MaterielApp and my AppLocalization.of(content) was null

https://github.com/flutter/flutter/issues/26365#issuecomment-523536339

Upvotes: 2

croxx5f
croxx5f

Reputation: 5743

You should add the AppLocalizations in your MaterialApp:

MaterialApp(
...
      localizationsDelegates: const [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
      ],
      supportedLocales: AppLocalizations.supportedLocales,
...

Upvotes: 24

Related Questions