专栏名称: 知识小集
目录
相关文章推荐
蒲公英Ouryao  ·  这个「取样助手」,省时省心,实用→ ·  昨天  
解螺旋  ·  真的不是DeepSeek!只要四步,还能自动 ... ·  2 天前  
Clinic門诊新视野  ·  探索丨3D ... ·  2 天前  
数据派THU  ·  数据派志愿者招募 | 寻找最志同道合的你! ·  3 天前  
51好读  ›  专栏  ›  知识小集

使用 Flutter 开发 Google Translate 程序

知识小集  · 掘金  ·  · 2019-02-22 06:39

正文

阅读 133

使用 Flutter 开发 Google Translate 程序

原文链接

| 作者:Guillaume Belouin

| 链接: blog.usejournal.com/flutter-goo…

直入主题吧。在本教程中,我们将使用 Flutter 来开发适用于 Android 和 iOS 的谷歌翻译应用程序。下面是程序的基本界面。

创建工程

要创建项目,我们必须运行 Android Studio 并单击 Start a new Flutter project ,然后选择 Flutt2 er application 。我们在表单中填写项目名称, flutter SDK 的路径,项目位置和描述,如下所示:

清空代码

创建项目后,我们首先清理 main.dart 文件中生成的代码。原本有一些代码,但我们希望尽可能简单。

import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google Translate',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        primaryColor: Colors.blue[600],
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Google Translate'),
          elevation: 0.0,
        ),
        body: Center(
          child: Text("We are going to translate everything !"),
        ),
      ),
    );
  }
}
复制代码

这段代码更干净且更容易理解。 MaterialApp 类为我们的应用程序定义了设计外观。然后在 ThemeData 类中添加蓝色主题,以便更接近真实的 Google Translate 应用程序。 Scaffold 创建应用程序的全局结构,它包含一个 AppBar 和一个 body body 部分是我们在 AppBar 下显示应用程序内容的地方。

组织代码

我的代码以这种方式组织,包含 components、screens、models 和 services。这只是一种组织方式,还有其它的方式,就看自己是怎么考虑。test 文件夹也以相同方式组织。

创建第一个组件

我们将创建第一个组件,来显示我们输入的文本的语言,以及要翻译成的语言。

import 'package:flutter/material.dart';
class ChooseLanguage extends StatefulWidget {
  ChooseLanguage({Key key}) : super(key: key);
  @override
  _ChooseLanguageState createState() => _ChooseLanguageState();
}
class _ChooseLanguageState extends State<ChooseLanguage> {
  String _firstLanguage = "English";
  String _secondLanguage = "French";
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
复制代码

这段代码创建了一个 ChooseLanguage 组件,该组件定义了两个变量来用于表示选择的语言。

@override
Widget build(BuildContext context) {
  return Container(
    height: 55.0,
    decoration: BoxDecoration(
      color: Colors.white,
      border: Border(
        bottom: BorderSide(
          width: 0.5,
          color: Colors.grey[500],
        ),
      ),
    ),
  );
}
复制代码

我们为容器定义特定高度和装饰以对组件进行样式设置。在 BoxDecoration 中,我们定义背景颜色和容器边框,以在组件底部显示一条分隔线。

接着我们将添加一个 Row 元素,它将帮助我们在一行上显示我们的 widgets 。然后,我们可以确定如何对齐此行中的元素。实际上,row 有两个轴,主轴与 row 方向相同,横轴与其方向交叉。操纵这两个轴,我们可以很容易地以我们想要的方式显示我们的组件。以下模式显示了 widget 如何使用不同属性进行显示。

在 Flutter 中,可以很容易重建这个显示,我们所需要的只是一个 Row 元素,并定义主轴和横轴对齐方式。 Expanded 元素将被添加到 Row 的子 widget 中。

@override
Widget build(BuildContext context) {
  return Container(
    ...
    child: Row(
      mainAxisAlignment: MainAxisAlignment.start,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        ...
      ],
    ),
  );
}
复制代码

现在是时候在我们的 Row 中添加 widget 了。我们需要在 InkWell 组件内简单地创建一个 Text 组件。 InkWell 组件将在文本周围创建一个可单击的区域,并显示 splash 效果。

child: Row(
  mainAxisAlignment: MainAxisAlignment.start,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: <Widget>[
    Expanded(
      child: Material(
        color: Colors.white,
        child: InkWell(
          onTap: () {},
          child: Center(
            child: Text(
              this._firstLanguage,
              style: TextStyle(
                color: Colors.blue[600],
                fontSize: 15.0,
              ),
            ),
          ),
        ),
      ),
    ),
    ...
    Expanded(
      child: Material(
        color: Colors.white,
        child: InkWell(
          onTap: () {},
          child: Center(
            child: Text(
              this._secondLanguage,
              style: TextStyle(
                color: Colors.blue[600],
                fontSize: 15.0,
              ),
            ),
          ),
        ),
      ),
    ),
  ],
),
复制代码

我们使用 Expanded 元素来构建包含白色背景颜色的 Text InkWell 元素需要子树中的 Material 来显示 splash 效果。 onTap 事件暂时不使用,我们稍后会在点击 Text 时更改语言。我们将 Text 显示在 Center 中,因为 Expanded 将占用所有空间,而我们希望将文本居中。最后,我们添加了变量 firstLanguage secondLanguage 来显示语言。

最后一步是创建一个图标按钮,以像谷歌翻译应用程序中那样切换语言。这很简单,我们将创建一个带有白色背景的 Material ,来包含我们的 IconButton 。我们在 Flutter 库中已经存在的图标列表中选择我们的图标。我选择使用 Icons.compare_arrows ,它与实际应用程序中的不完全相同。

<Widget>[
  Expanded(
    ...
  ),
  Material(
    color: Colors.white,
    child: IconButton(
      icon: Icon(
        Icons.compare_arrows,
        color: Colors.grey[700],
      ),
      onPressed: () {},
    ),
  ),
  Expanded(
    ...
  ),
]
复制代码

现在我们的组件终于完成了,我们需要将它添加到页面中。然后我们应该能看到下面的效果。

import 'package:flutter/material.dart';

import '../components/choose-language.dart';

class HomePage extends StatefulWidget {
  HomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _HomePageState extends State<HomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        elevation: 0.0,
      ),
      body: Column(
        children: <Widget>[
          ChooseLanguage(),
      ),
    );
  }
}
复制代码

翻译操作的 UI

我们现在需要创建组件来选择我们想要翻译的文本。在 Google 应用程序中,我们必须输入文本或使用其它的方式,例如拍照。我们遵循与 iOS 应用程序相同的设计。因此,我们将在本教程的下一部分中创建一个可点击的 widget。

我们将使用 Column Row 创建具有此结构的组件以显示我们的 UI。 Column 的工作方式与 Row 的工作方式相同,唯一的区别是它显示元素的方向。我们现在将在一个新组件中编写我们想要实现的结构。

import 'package:flutter/material.dart';

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

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

class _TranslateTextState extends State<TranslateText> {
  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.white,
      margin: EdgeInsets.all(0.0),
      elevation: 2.0,
      child: Container(
        height: 150.0,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Expanded(...),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                Material(
                  color: Colors.white,
                  child: Column(
                     children: <Widget>[...],
                   ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
复制代码

我们还为 Column Row 定义了主轴和交叉轴的对齐方式。在这里,我们选择了 MainAxisAlignment.spaceBetween ,因为我们希望在可点击的图标之间留出一些空间。

由于基本结构已完成,现在可以专注于输入部分。输入部分不是我们将在其中输入文本的 widget。在实际的应用中,当我们点击这部分时,会出现一个输入框来输入文本。

class _TranslateTextState extends State<TranslateText> {
  @override
  Widget build(BuildContext context) {
    return Card(
      ...,
      child: Container(
        height: 150.0,
        child: Column(
          ...,
          children: <Widget>[
            Expanded(
              child: InkWell(
                onTap: () {},
                child: Container(
                  width: double.infinity,
                  padding: EdgeInsets.only(
                    left: 16.0, 
                    right: 16.0, 
                    top: 16.0
                  ),
                  child: Text(
                    "Enter text",
                    style: TextStyle(
                      color: Colors.grey[700],
                    ),
                  ),
                ),
              ),
            ),
            Row(...),
          ],
        ),
      ),
    );
  }
}
复制代码

这段代码与我们创建的第一个组件非常相似。是一个简单的 InkWell 内部包含一个 Text 。我们将保留 onTap 函数,就像我们在创建的第一个组件中所做的那样。我们现在要在 Row 中创建可点击的图标。

我们将使用相同的代码来显示带有描述性文本的 4 个图标。我们可以在 Row 中编写相同 4 份代码,但是如果我们这样做,就会有很多重复代码。

使用此方法,在更改代码必须重复修改很多处。最好的解决方案是创建另一个组件,我们将使用不同的参数调用四次。

import 'package:flutter/material.dart';

class ActionButton extends StatefulWidget {
  ActionButton({Key key, this.icon, this.text, this.imageIcon}) : super(key: key);

  final IconData icon;
  final AssetImage imageIcon;
  final String text;

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

class _ActionButtonState extends State<ActionButton> {

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.white,
      child: FlatButton(
        padding: EdgeInsets.only(
          left: 8.0,
          right: 8.0,
          top: 2.0,
          bottom: 2.0,
        ),
        onPressed: () {},
        child: Column(
          children: <Widget>[...],
        ),
      ),
    );
  }
}
复制代码

创建可点击图标

我们使用了新的组件 ActionButton ,与其他组件不同,我们添加了参数 icon,imageIcon 和 text。 我使用的一些图标不在谷歌的库中,因此我创建了自己的图标。

这就是我区分图标和 ImageIcon 的原因。我们将创建一个函数来显示 IconData AssetImage 中的图标。

Widget _displayIcon() {
  if (this.widget.icon != null) {
    return Icon(
      this.widget.icon,
      size: 23.0,
      color: Colors.blue[800],
    );
  } else if (this.widget.imageIcon != null) {
    return ImageIcon(
      this.widget.imageIcon,
      size: 23.0,
      color: Colors.blue[800],
    );
  } else {
    return Container();
  }
}
复制代码

此函数检查 icon imageIcon 变量是否为 null,当其中一个变量不为 null 时,我们将使用右侧组件显示图像。事实上,我们通过 Icon 组件显示 IconData AssetImage 基于 ImageIcon 组件。最后如果两者都为 null,则显示一个空容器。

@override
Widget build(BuildContext context) {
  return Material(
    color: Colors.white,
    child: FlatButton(
      ...,
      child: Column(
        children: <Widget>[
          _displayIcon(),
          Text(
            this.widget.text,
            style: TextStyle(fontSize: 12),
          ),
        ],
      ),
    ),
  );
}
复制代码

我们在 Column 中添加了 _displayIcon 函数。它将使用我们要传递给组件的图标或图像图标中显示正确的 widget。我们现在可以在之前创建的 TranslateText 中调用我们的组件了。

import 'package:flutter/material.dart';

import 'ActionButton.dart';

class _TranslateTextState extends State<TranslateText> {
  @override
  Widget build(BuildContext context) {
    return Card(
      ...,
      child: Container(
        height: 150.0,
        child: Column(
          ...,
          children: <Widget>[
            Expanded(
              ...,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                ActionButton(
                  icon: Icons.camera_alt,
                  text: "Camera",
                ),
                ActionButton(
                  imageIcon: AssetImage("assets/pen.png"),
                  text: "Handwriting",
                ),
                ActionButton(
                  imageIcon: AssetImage("assets/conversation.png"),
                  text: "Conversation",
                ),
                ActionButton(
                  icon: Icons.keyboard_voice,
                  text: "Voice",
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
复制代码

我们导入组件,使用不同的参数调用 ActionButton 4 次,以便为每个 ActionButton 提供唯一的渲染。

添加图片资源(可选)

我在我们刚制作的一些按钮中添加了自己的图像,但我们需要更改根文件夹中的文件 pubspec.yaml 来引入这些图像。

flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/






请到「今天看啥」查看全文