Language Translation using MLKit in Flutter - Part 2

Language Translation is a difficult task and training our model for it makes it even Harder. But Google MLKit makes the task easier. Google's machine learning expertise is now available to mobile developers with Google ML Kit. This package is easy to use and powerful, offering solutions optimized for device performance on both iOS and Android. In the previous article, Language Translation using MLKit in Flutter - Part 1 (allaboutflutter.com), we learnt to use MLKit and did a basic project.

Now we will learn to make a Language Translation app. The final output will look as follows:

Fully Functional Translation App

Now we have learnt to use the API of MLKit for translation, we will now use it to translate from any one language to another.

Basic Functions

Each time we change the source and target language, we need to reinitialize the translator, hence a method will be best for it as follows.

void setTranslator(TranslateLanguage source, TranslateLanguage target) {
  onDeviceTranslator = OnDeviceTranslator(
    sourceLanguage: source,
    targetLanguage: target,
  );
}

We use the following translate function. It is the same from last tutorial.

Future<String> translateText(String sourceText) async {
  final String translation =
      await onDeviceTranslator.translateText(sourceText);
  return translation;
}

List of languages

First, we will specify and fetch all the languages and store them in a List.

final List<String> languages =
    TranslateLanguage.values.map((language) => language.name).toList();

Next as before, we will have source and target languages. By default, we can put any language of your choice.

TranslateLanguage sourceLanguage = TranslateLanguage.english;
TranslateLanguage targetLanguage = TranslateLanguage.hindi;

We need to use a Dropdown button as follows:

DropdownButton<String>(
  value: ,
  onChanged: (String? newValue) {},
  items: ,
)

Now we first need to specify the value, which will be either the source or target language name. We will do this here for the source, a similar would apply to the target. We use the sourceLanguage.name to get the name of the language.

value: sourceLanguage.name,

Next, we need items in the dropdown. So we need to iterate over the languages and create a DropdownMenuItem.

items: languages.map(
  (String item) {
    return DropdownMenuItem<String>(
      value: item,
      child: Text(item),
    );
  },
).toList(),

Whenever we change the language by selecting from the dropdown, we need to reassign the source/target language as well as set the translator with a new source-target language pair. Hence we first change the language based on the name from dropdown and later change the translator.

onChanged: (String? newValue) {
  if (newValue == null) return;
  setState(
    () {
      _textEditingController1.clear();
      _textEditingController2.clear();
      sourceLanguage = TranslateLanguage.values
          .firstWhere((language) => language.name == newValue);
    },
  );
  setTranslator(sourceLanguage, targetLanguage);
},

We do a clear the _textEditingController1 text because when we change language, the older language text would stay and it will not look nice. Finally, we put our TextField with multiline. We will implement them in the next section.

Text Input Editor

Also, we need to TextEdittingController since now we will manage two TextFields although one will be editable.

final TextEditingController _textEditingController1 = TextEditingController();
final TextEditingController _textEditingController2 = TextEditingController();

Then we create two TextFields, one for source and one for target. We keep source editable why target not editable because the translation is one way in our app.

Expanded(
  child: TextField(
    controller: _textEditingController1,
    maxLines: null,
    minLines: 100,
    keyboardType: TextInputType.multiline,
    decoration: const InputDecoration(
      hintText: 'Enter text to translate',
      border: OutlineInputBorder(),
    ),
    onChanged: (value) async {
      _translatedText = await translateText(value);
      setState(() {
        _textEditingController2.text = _translatedText;
      });
    },
  ),
),

Here is the final code.

import 'package:flutter/material.dart';
import 'package:google_mlkit_translation/google_mlkit_translation.dart';

class CompleteTranslationTutorial extends StatefulWidget {
  const CompleteTranslationTutorial({super.key});

  @override
  State<CompleteTranslationTutorial> createState() =>
      _CompleteTranslationTutorialState();
}

class _CompleteTranslationTutorialState
    extends State<CompleteTranslationTutorial> {
  TranslateLanguage sourceLanguage = TranslateLanguage.english;
  TranslateLanguage targetLanguage = TranslateLanguage.hindi;

  final TextEditingController _textEditingController1 = TextEditingController();
  final TextEditingController _textEditingController2 = TextEditingController();
  final List<String> languages =
      TranslateLanguage.values.map((language) => language.name).toList();
  String _translatedText = '';
  late OnDeviceTranslator onDeviceTranslator;

  Future<String> translateText(String sourceText) async {
    final String translation =
        await onDeviceTranslator.translateText(sourceText);
    return translation;
  }

  void setTranslator(TranslateLanguage source, TranslateLanguage target) {
    onDeviceTranslator = OnDeviceTranslator(
      sourceLanguage: source,
      targetLanguage: target,
    );
  }

  @override
  void initState() {
    super.initState();
    setTranslator(sourceLanguage, targetLanguage);
  }

  @override
  void dispose() {
    onDeviceTranslator.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Translation Tutorial'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            DropdownButton<String>(
              value: sourceLanguage.name,
              onChanged: (String? newValue) {
                if (newValue == null) return;
                setState(
                  () {
                    _textEditingController1.clear();
                    _textEditingController2.clear();
                    sourceLanguage = TranslateLanguage.values
                        .firstWhere((language) => language.name == newValue);
                  },
                );
                setTranslator(sourceLanguage, targetLanguage);
              },
              items: languages.map(
                (String item) {
                  return DropdownMenuItem<String>(
                    value: item,
                    child: Text(item),
                  );
                },
              ).toList(),
            ),
            Expanded(
              child: TextField(
                controller: _textEditingController1,
                maxLines: null,
                minLines: 100,
                keyboardType: TextInputType.multiline,
                decoration: const InputDecoration(
                  hintText: 'Enter text to translate',
                  border: OutlineInputBorder(),
                ),
                onChanged: (value) async {
                  _translatedText = await translateText(value);
                  setState(() {
                    _textEditingController2.text = _translatedText;
                  });
                },
              ),
            ),
            const Divider(),
            DropdownButton<String>(
              value: targetLanguage.name,
              onChanged: (String? newValue) {
                if (newValue == null) return;
                setState(() {
                  _textEditingController1.clear();
                  _textEditingController2.clear();
                  targetLanguage = TranslateLanguage.values
                      .firstWhere((language) => language.name == newValue);
                });
                setTranslator(sourceLanguage, targetLanguage);
              },
              items: languages.map((String item) {
                return DropdownMenuItem<String>(
                  value: item,
                  child: Text(item),
                );
              }).toList(),
            ),
            Expanded(
              child: TextField(
                controller: _textEditingController2,
                maxLines: null,
                minLines: 100,
                keyboardType: TextInputType.none,
                decoration: const InputDecoration(
                  hintText: 'Translated Text will appear here',
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            const Divider(),
          ],
        ),
      ),
    );
  }
}

Output

Hope you enjoyed it.

Did you find this article valuable?

Support All About Flutter | Flutter and Dart by becoming a sponsor. Any amount is appreciated!