Bạn sẽ xây dựng ứng dụng như thế nào trong part 1?
Ứng dụng của bạn sẽ là một ứng dụng đơn giản, tạo ra các tên đề xuất cho một công ty start up. User có thể select và unselect tên, lưu lại cái tên họ thích nhất. App sẽ tạo các tên một cách từ từ. Khi user scroll, sẽ có nhiều tên hơn được tạo ra, không giới hạn.
Step 1: Tạo Flutter app
Tạo một ứng dụng Flutter cơ bản dựa trên template có sẵn, đặt tên là startup_namer thay vì myapp
- Thay đổi code trong lib/main.dart.
Xóa tất cả code trong lib/main.dart, thay bằng đoạn code sau. Hiển thị “Hello World” ở giữa màn hình.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <span class="token comment">// Copyright 2018 The Flutter team. All rights reserved.</span> <span class="token comment">// Use of this source code is governed by a BSD-style license that can be</span> <span class="token comment">// found in the LICENSE file.</span> <span class="token keyword">import</span> <span class="token string">'package:flutter/material.dart'</span><span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">runApp</span><span class="token punctuation">(</span><span class="token function">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">MyApp</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token string">'Welcome to Flutter'</span><span class="token punctuation">,</span> home<span class="token punctuation">:</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span> appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'Welcome to Flutter'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token function">Center</span><span class="token punctuation">(</span> child<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'Hello World'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Chạy ứng dụng. Bạn có thể thấy cả Android và iOS như sau, tùy thuộc vào device bạn sử dụng.
Quan sát
- Ví dụ này tạo ra một ứng dụng Material. Material là ngôn ngữ thiết kế trực quan đạt tiêu chuẩn trên thiết bị di động và web. Flutter cung cấp một bộ Material phong phú.
- Phương thức main() sử dụng ký hiệu mũi tên (=>). Sử dụng ký hiệu mũi tên cho các hàm hoặc phương thức một dòng.
- Ứng dụng extend StatlessWidget làm cho ứng dụng trở thành một widget. Trong Flutter, hầu hết mọi thứ là một widget, bao gồm căn chỉnh, phần đệm và bố cục.
- Scaffold widget, từ Material library, cung cấp một thanh ứng dụng mặc định, tiêu đề và thuộc tính body giữ cây widget cho màn hình chính. Các widget con có thể khá phức tạp.
- Công việc chính của widget là cung cấp một phương thức build() mô tả cách hiển thị widget theo các tiện ích khác, cấp thấp hơn.
- Phần body của ví dụ này bao gồm một Center widget chứa widget con Text. Widget Center căn chỉnh widget con vào giữa màn hình.
Step 2: Sử dụng external package
Trong bước này, bạn sẽ bắt đầu sử dụng open package có tên english_words, chứa một vài ngàn từ tiếng Anh được sử dụng nhiều nhất cùng với một số chức năng tiện ích.
Bạn có thể tìm thấy gói english_words, cũng như nhiều gói nguồn mở khác, trên pub.dev.
- Tệp pubspec quản lý các assets và dependencies cho ứng dụng Flutter. Trong pubspec.yaml, thêm english_words (3.1.0 trở lên) vào danh sách dependencies:
1 2 3 4 5 6 7 | dependencies<span class="token punctuation">:</span> flutter<span class="token punctuation">:</span> sdk<span class="token punctuation">:</span> flutter cupertino_icons<span class="token punctuation">:</span> <span class="token operator">^</span><span class="token number">0.1</span><span class="token number">.2</span> english_words<span class="token punctuation">:</span> <span class="token operator">^</span><span class="token number">3.1</span><span class="token number">.0</span> # add <span class="token keyword">this</span> line |
- Trong khi xem pubspec trong editor view của Android Studio, hãy nhấp vào Packages get. Việc này sẽ kéo package vào project của bạn. Bạn sẽ thấy những điều sau đây trong bảng điều khiển:
1 2 3 4 | flutter pub get Running <span class="token string">"flutter pub get"</span> in startup_namer<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> Process finished with exit code <span class="token number">0</span> |
Thực hiện Packages get cũng tự động tạo tệp pubspec.lock với danh sách tất cả các package được pull vào project và phiên bản của chúng.
- Trong lib/main.dart, import package:
1 2 3 | <span class="token keyword">import</span> <span class="token string">'package:flutter/material.dart'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'package:english_words/english_words.dart'</span><span class="token punctuation">;</span> |
Khi bạn nhập, Android Studio cung cấp cho bạn các đề xuất cho các thư viện để nhập. Sau đó, nó kết xuất chuỗi nhập màu xám, cho bạn biết rằng thư viện đã nhập không được sử dụng (cho đến bây giờ).
- Sử dụng English words để tạo văn bản thay vì sử dụng “Hello World”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <span class="token keyword">import</span> <span class="token string">'package:flutter/material.dart'</span><span class="token punctuation">;</span> <span class="token keyword">import</span> <span class="token string">'package:english_words/english_words.dart'</span><span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">runApp</span><span class="token punctuation">(</span><span class="token function">MyApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">MyApp</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> wordPair <span class="token operator">=</span> WordPair<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Add this line.</span> <span class="token keyword">return</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token string">'Welcome to Flutter'</span><span class="token punctuation">,</span> home<span class="token punctuation">:</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span> appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'Welcome to Flutter'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token function">Center</span><span class="token punctuation">(</span> <span class="token comment">//child: Text('Hello World'), // Replace this text...</span> child<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span>wordPair<span class="token punctuation">.</span>asPascalCase<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// With this text.</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Nếu ứng dụng đang chạy, hãy hot reload để cập nhật ứng dụng đang chạy. Mỗi lần bạn nhấp vào hot reload hoặc save the project, bạn sẽ thấy một cặp từ khác nhau, được chọn ngẫu nhiên, trong ứng dụng đang chạy. Điều này là do việc ghép từ được tạo bên trong phương thức xây dựng, được chạy mỗi khi MaterialApp yêu cầu kết xuất hoặc khi chuyển đổi Nền tảng trong Flutter Inspector.
Step 3: Add Stateful widget
Stateless widgets à bất biến, có nghĩa là các thuộc tính của chúng không thể thay đổi, tất cả các giá trị là cuối cùng.
Stateful widgets có trạng thái duy trì trạng thái có thể thay đổi trong suốt vòng đời của widget. Việc triển khai một widget Stateful yêu cầu ít nhất hai lớp: 1) một lớp StatefulWidget tạo ra một thể hiện của 2) một lớp State. Bản thân lớp StatefulWidget là bất biến, nhưng lớp State vẫn tồn tại trong suốt vòng đời của widget.
Trong bước này, bạn sẽ thêm một stateful widget, RandomWords, tạo lớp State của nó, RandomWordsState. Sau đó, bạn sẽ sử dụng RandomWords như là con bên trong MyApp stateless widget hiện có.
- Tạo một state class. tối thiểu. Thêm phần sau vào dưới cùng của main.dart:
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">RandomWordsState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token generics function"><span class="token punctuation"><</span>RandomWords<span class="token punctuation">></span></span> <span class="token punctuation">{</span> <span class="token comment">// TODO Add build() method</span> <span class="token punctuation">}</span> |
Lưu ý khai báo State<RandomWords>. Điều này chỉ ra rằng chúng ta sử dụng chung State class cho RandomWords. Hầu hết các logic của ứng dụng và states được đặt tại đây, nó duy trì trạng thái cho widget RandomWords. Lớp này lưu các cặp từ được tạo, phát triển vô hạn khi người dùng cuộn và các cặp từ yêu thích (trong phần 2), khi người dùng thêm hoặc xóa chúng khỏi danh sách bằng cách bật biểu tượng trái tim.
RandomWordsState phụ thuộc vào lớp RandomWords. Bạn sẽ thêm điều đó tiếp theo.
- Thêm stateful RandomWords widget vào main.dart. RandomWords widget không làm gì khác ngoài việc tạo lớp State của nó:
1 2 3 4 5 | <span class="token keyword">class</span> <span class="token class-name">RandomWords</span> <span class="token keyword">extends</span> <span class="token class-name">StatefulWidget</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> RandomWordsState <span class="token function">createState</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">RandomWordsState</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Sau khi thêm lớp state, IDE sẽ báo rằng lớp đó thiếu phương thức build. Tiếp theo, bạn sẽ thêm một phương thức build cơ bản để tạo các cặp từ bằng cách di chuyển code tạo từ từ MyApp sang RandomWordsState.
- Thêm phương thức build vào RandomWordsState, như được hiển thị bên dưới:
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">RandomWordsState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token operator"><</span>RandomWords<span class="token operator">></span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> <span class="token comment">// Add from this line ... </span> Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> WordPair wordPair <span class="token operator">=</span> WordPair<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">Text</span><span class="token punctuation">(</span>wordPair<span class="token punctuation">.</span>asPascalCase<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// ... to this line.</span> <span class="token punctuation">}</span> |
- Xóa code tạo từ khỏi MyApp bằng cách thực hiện các thay đổi bên dưới:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token keyword">class</span> <span class="token class-name">MyApp</span> <span class="token keyword">extends</span> <span class="token class-name">StatelessWidget</span> <span class="token punctuation">{</span> <span class="token metadata symbol">@override</span> Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">final</span> WordPair wordPair <span class="token operator">=</span> WordPair<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Delete this line.</span> <span class="token keyword">return</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token string">'Welcome to Flutter'</span><span class="token punctuation">,</span> home<span class="token punctuation">:</span> <span class="token function">Scaffold</span><span class="token punctuation">(</span> appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'Welcome to Flutter'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token function">Center</span><span class="token punctuation">(</span> <span class="token comment">//child: Text(wordPair.asPascalCase), // Change this line to... </span> child<span class="token punctuation">:</span> <span class="token function">RandomWords</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// ... this line.</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Hot reload app. App sẽ hoạt động như trước, hiển thị một từ ghép mỗi khi bạn hot reload hoặc save project.
Step 4: Tạo ListView scroll vô hạn
Trong bước này, bạn sẽ mở rộng RandomWordsState để tạo và hiển thị danh sách các từ ghép. Khi người dùng cuộn, danh sách (được hiển thị trong widget ListView) sẽ tăng lên vô hạn. Hàm khởi tạo factory builder của ListView cho phép bạn xây dựng chế độ xem lazy list, theo yêu cầu.
- Thêm list _suggestions vào lớp RandomWordsState để lưu các từ ghép được đề xuất. Ngoài ra, thêm một biến _biggerFont để làm cho kích thước phông chữ lớn hơn.
1 2 3 4 5 6 7 | <span class="token keyword">class</span> <span class="token class-name">RandomWordsState</span> <span class="token keyword">extends</span> <span class="token class-name">State</span><span class="token operator"><</span>RandomWords<span class="token operator">></span> <span class="token punctuation">{</span> <span class="token comment">// Add the next two lines.</span> <span class="token keyword">final</span> List<span class="token operator"><</span>WordPair<span class="token operator">></span> _suggestions <span class="token operator">=</span> <span class="token operator"><</span>WordPair<span class="token operator">></span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">final</span> TextStyle _biggerFont <span class="token operator">=</span> <span class="token keyword">const</span> <span class="token function">TextStyle</span><span class="token punctuation">(</span>fontSize<span class="token punctuation">:</span> <span class="token number">18</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> |
Tiếp theo, bạn sẽ thêm hàm _buildSuggestions() vào lớp RandomWordsState. Phương thức này sẽ xây dựng ListView hiển thị ghép nối từ được đề xuất.
Lớp ListView cung cấp một thuộc tính builder, itemBuilder, đó là hàm factory builder và hàm callback được chỉ định là một hàm ẩn danh. Hai tham số được truyền cho hàm là BuildContext và iterator row, i. Vòng lặp bắt đầu từ 0 và tăng mỗi lần hàm được gọi là một lần cho mỗi lần ghép từ được đề xuất. Mô hình này cho phép danh sách gợi ý phát triển vô hạn khi người dùng cuộn.
- Thêm toàn bộ hàm _buildSuggestions, được hiển thị bên dưới, vào lớp RandomWordsState:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Widget <span class="token function">_buildSuggestions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> ListView<span class="token punctuation">.</span><span class="token function">builder</span><span class="token punctuation">(</span> padding<span class="token punctuation">:</span> <span class="token keyword">const</span> EdgeInsets<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">,</span> itemBuilder<span class="token punctuation">:</span> <span class="token punctuation">(</span>BuildContext _context<span class="token punctuation">,</span> int i<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>i<span class="token punctuation">.</span>isOdd<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Divider</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">final</span> int index <span class="token operator">=</span> i <span class="token operator">~/</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>index <span class="token operator">>=</span> _suggestions<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span> _suggestions<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span><span class="token function">generateWordPairs</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">take</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token function">_buildRow</span><span class="token punctuation">(</span>_suggestions<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Hàm _buildSuggestions gọi _buildRow một lần cho mỗi cặp từ. Hàm này hiển thị mỗi cặp mới trong ListTile.
- Thêm hàm _buildRow vào RandomWordsState:
1 2 3 4 5 6 7 8 9 | Widget <span class="token function">_buildRow</span><span class="token punctuation">(</span>WordPair pair<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">ListTile</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span> pair<span class="token punctuation">.</span>asPascalCase<span class="token punctuation">,</span> style<span class="token punctuation">:</span> _biggerFont<span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
- Cập nhật phương thức build cho RandomWordsState để sử dụng _buildSuggestions(), thay vì gọi trực tiếp thư viện tạo từ. (Scaffold thực hiện Material Design visual layout cơ bản.)
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token metadata symbol">@override</span> Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">//final wordPair = WordPair.random(); // Delete these... </span> <span class="token comment">//return Text(wordPair.asPascalCase); // ... two lines.</span> <span class="token keyword">return</span> Scaffold <span class="token punctuation">(</span> <span class="token comment">// Add from here... </span> appBar<span class="token punctuation">:</span> <span class="token function">AppBar</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token function">Text</span><span class="token punctuation">(</span><span class="token string">'Startup Name Generator'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token function">_buildSuggestions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ... to here.</span> <span class="token punctuation">}</span> |
- Update phương thức build của MyApp, thay đổi title và đặt home là RandomWords widget:
1 2 3 4 5 6 7 8 | <span class="token metadata symbol">@override</span> Widget <span class="token function">build</span><span class="token punctuation">(</span>BuildContext context<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">MaterialApp</span><span class="token punctuation">(</span> title<span class="token punctuation">:</span> <span class="token string">'Startup Name Generator'</span><span class="token punctuation">,</span> home<span class="token punctuation">:</span> <span class="token function">RandomWords</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
- Restart app. Kết quả như sau:
Thế là đã xong part 1 của app này. Phần 2 chúng ta sẽ thêm một chút như sau:
- Thêm tương tác.
- Thêm khả năng điều hướng đến một màn hình mới.
- Thay đổi màu sắc theme.
Demo:
Cảm ơn các bạn đã đọc bài của mình