原文連接數組
| 做者:Guillaume Belouin微信
| 連接:blog.usejournal.com/flutter-goo…app
直入主題吧。在本教程中,咱們將使用 Flutter 來開發適用於 Android 和 iOS 的谷歌翻譯應用程序。下面是程序的基本界面。less
要建立項目,咱們必須運行 Android Studio 並單擊 Start a new Flutter project
,而後選擇 Flutt2 er application
。咱們在表單中填寫項目名稱,flutter SDK
的路徑,項目位置和描述,以下所示:ide
建立項目後,咱們首先清理 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
下顯示應用程序內容的地方。flex
個人代碼以這種方式組織,包含 components、screens、models 和 services。這只是一種組織方式,還有其它的方式,就看本身是怎麼考慮。test 文件夾也以相同方式組織。ui
咱們將建立第一個組件,來顯示咱們輸入的文本的語言,以及要翻譯成的語言。this
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
組件,該組件定義了兩個變量來用於表示選擇的語言。google
@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(),
),
);
}
}
複製代碼
咱們如今須要建立組件來選擇咱們想要翻譯的文本。在 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/
複製代碼
我在 pubspec.yaml
文件中添加了 - assets /
,並在個人圖像的根文件夾中建立了一個 assets
文件夾。完成此操做後,可能須要從新運行該應用程序。最後,你能夠像我以前那樣調用你的圖像,例如:AssetImage("assets/pen.png")
。
若是您想了解有關 assert 的更多信息以及咱們如何使用它們,請您閱讀 Flutter 文檔。
看當作果
第二個組件完成後,應用程序的 UI 如今幾乎完成了。
咱們須要再建立一個新組件來顯示最近的翻譯列表。仍然使用相同的原理,咱們將開發一個組合行和列的 widget。List
能夠像 Column 同樣顯示咱們的項目,不過更加自動化,而不是添加 n 次組件。此外,能夠在 List 上滑動。
首先,我定義了一個 Translate
類,它由咱們在列表中顯示項目所需的元素組成。
class Translate {
String text;
String translatedText;
bool isStarred;
Translate(String text, String translated, bool isStarred) {
this.text = text;
this.translatedText = translated;
this.isStarred = isStarred;
}
}
複製代碼
而後,咱們能夠建立一個名爲 ListTranslate
的新組件。 咱們在 itemCount
中定義 ListView
將顯示的行數。在 itemBuilder
中,咱們使用數組列表顯示行。
import 'package:flutter/material.dart';
import '../models/translate.dart';
class ListTranslate extends StatefulWidget {
ListTranslate({Key key}) : super(key: key);
@override
_ListTranslateState createState() => _ListTranslateState();
}
class _ListTranslateState extends State<ListTranslate> {
List<Translate> _items = [];
Widget _displayCard(int index) {
return Card(
child: Container(
),
);
}
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (BuildContext ctxt, int index) {
return _displayCard(index);
},
),
);
}
}
複製代碼
下一步是建立列表的項目,就像咱們以前製做的草圖同樣。爲此,咱們將使用 Row 和 Column,就像咱們爲最後的組件所作的那樣。不過,咱們將添加一些樣式,如邊框半徑,邊距和填充,讓它更好看。
Widget _displayCard(int index) {
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(0.0)),
),
margin: EdgeInsets.only(left: 8.0, right: 8.0, top: 0.5),
child: Container(
height: 80.0,
padding: EdgeInsets.only(left: 16.0, top: 16.0, bottom: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text(
_items[index].text,
style: TextStyle(
fontWeight: FontWeight.w600,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
Text(
_items[index].translatedText,
style: TextStyle(
fontWeight: FontWeight.w400,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
IconButton(
onPressed: () {},
icon: Icon(
Icons.star_border,
size: 23.0,
color: Colors.grey[700],
),
),
],
),
),
);
}
複製代碼
這將顯示列表中的每一行,以展現搜索的信息。Flexible
小部件將其高度和寬度擴展到最大值。因爲咱們在 Text
中添加了 maxLine
和 overflow
,若是咱們的文本太長,則會被截斷。
IconButton(
onPressed: () {},
icon: Icon(
_items[index].isStarred ? Icons.star : Icons.star_border,
size: 23.0,
color: _items[index].isStarred ? Colors.blue[600] : Colors.grey[700],
),
),
複製代碼
若是行的元素已加星標,則咱們想要更改顏色和圖標。咱們使用三元運算符直接從列表信息中更改它。
List<Translate> _items = [
Translate(
"yellowish",
"jaunâtre",
true,
),
...,
];
複製代碼
咱們暫時使用一些信息填充列表項,以查看它的外觀。如今,設計終於完成了,咱們能夠觀察咱們在整個教程期間所作的工做。
這篇教程展現了使用 Flutter 爲 Android 和 iOS 建立移動應用程序有多簡單快捷。咱們還發現一些部分看起來像 Web 開發中 flexbox
。即便這是一個移動 App 的開發,Web 開發人員也能夠快速地瞭解所作的事情。咱們目前只作了一個沒有任何功能的基本應用程序,可是編寫下一個部分並不困難!
歡迎關注咱們的公衆號:iOS-Tips,也歡迎加入咱們的羣組討論問題。能夠加微信 coldlight_hh
/wsy9871
進入咱們的 iOS
/flutter
微信羣。