Facebook Pixel

Cách tạo ứng dụng Cryptocurrency price list bằng Flutter SDK

10 Dec, 2021

Chau Le

Author

Trong hướng dẫn này, mình sẽ chỉ cho bạn cách sử dụng Flutter để xây dựng một ứng dụng hiển thị giá hiện tại của các loại tiền điện tử khác nhau. Mình sẽ hướng dẫn bạn qua các nguyên tắc cơ bản của Flutter và Dart.

Cách tạo ứng dụng Cryptocurrency price list bằng Flutter SDK

Mục Lục

(Lưu ý: Bài viết này được viết ở phiên bản dưới Flutter version 2.0).

Trong bài viết này, mình sẽ hướng dẫn cho bạn cách sử dụng Flutter để xây dựng một ứng dụng hiển thị giá hiện tại của các loại tiền điện tử khác nhau. Mình sẽ hướng dẫn bạn qua các nguyên tắc cơ bản của Flutter và Dart.

Đối với hướng dẫn này, mình sẽ sử dụng Android Studio, nhưng bạn cũng có thể sử dụng IntelliJ hoặc Visual Studio Code.

Bắt đầu nào

Trên Android Studio hoặc IntelliJ, nhấp vào File menu -> New -> New Flutter Project. Nếu bạn không thấy tùy chọn New Flutter Project, hãy đảm bảo rằng bạn đã cài đặt Flutter plugin. Nếu bạn đang sử dụng Visual Studio Code, hãy làm theo các bước ở đây để tạo một dự án mới.

Khi trang mở ra, chọn Ứng dụng Flutter và nhấp vào nút Next.

Trang tiếp theo cho phép bạn thiết lập dự án. Bạn có thể sử dụng một thiết lập tương tự như hình ảnh bên dưới. Chỉ cần đảm bảo rằng đường dẫn Flutter SDK trỏ đến thư mục mà bạn đã tải xuống Flutter.

Trang cuối cùng cho phép bạn thiết lập tên miền công ty của mình và bạn có thể đặt nó thành bất kỳ tên miền nào. Sau đó, nhấp vào nút Finish.

Việc tạo dự án sẽ bắt đầu sau khi nhấp vào nút Finish, thường mất vài phút.

Khi hoàn tất, dự án của bạn sẽ trông như thế này.

Một tệp có tên làmain.dartđã được tạo trong thư mụclib. Nó chứa code cho một ứng dụng demo. Vì chúng ta sẽ xây dựng ứng dụng của mình từ đầu, hãy mở tệpmain.dartvà delete/clear tất cả code trong đó.

Nếu dự án của bạn bao gồm một thư mụctestcó chứa tệpwidget_test.dart, hãy xóa tệp này trước khi tiếp tục. Nó chứa các unit test của đoạn code chúng ta vừa xóa.

Ứng dụng Flutter được viết bằng ngôn ngữ lập trình Dart. Tệpmain.dartlà Dart source file (phần mở rộng.dart). Quy ước của Dart là đặt tên cho các source file bằng cách sử dụnglowercase_with_underscores.

Hãy bắt đầu viết một số Dart code. Chúng ta sẽ bắt đầu với truyền thống lập trình: printing “Hello World!”

Để làm được điều đó, chúng ta sẽ phải tạo một thứ gọi là hàmmain (main function). Hàmmain(main function) là hàm (function) cấp cao nhất mà mọi ứng dụng Flutter có, đóng vai trò là entry point của ứng dụng. Hãy coi nó giống như lối vào một ngôi nhà.

Khi bạn chạy ứng dụng của mình trên thiết bị, quá trình thực thi sẽ bắt đầu từ hàmmain. Hãy tạo một hàmmain đơn giản, sau đó hãy nhập code vào tệpmain.dartcủa bạn.

Dart
// This is where the app starts executing.
void main() {
  print('Hello World'); // Prints Hello World! to the console
}

Như bạn có thể thấy, việc tạo hàmmainrất dễ dàng. Dòng thứ hai chứa khai báo hàmmain: kiểu trả về (void) và tên (main). Hàm main trả vềvoidnghĩa là nó không trả về gì cả.

Dòng thứ ba thực hiện việc in ra console. Chúng ta gọi hàmprintvà truyền một đối số chuỗi cho nó. Lưu ý rằng trong Dart, bạn có thể sử dụng dấu ngoặc kép (‘string’) hoặc dấu ngoặc kép (“string”) khi khai báo một chuỗi ký tự.

Để chạy code, hãy nhấp vào nút run (play) màu xanh lục ở đầu Android Studio hoặc IntelliJ. Hãy đảm bảo rằng bạn có một thiết bị thực được kết nối hoặc bạn có một emulator đang chạy.

Sau khi ứng dụng khởi động thành công, bạn sẽ thấy “Hello World!” được in trên bảng điều khiển (console).

Nhưng nếu bạn kiểm tra thiết bị hoặc emulator của mình, bạn sẽ thấy điều gì đó đáng thất vọng.

Chà, điều này đã được biết rõ từ trước, vì chúng ta hiện chỉ in ra bảng điều khiển. Không có gì được thêm vào giao diện người dùng ứng dụng và đó là lý do tại sao nó trống.

Vì vậy, hãy khắc phục điều này bằng cách thêm một số element vào giao diện người dùng ứng dụng. Ứng dụng của chúng ta sẽ sử dụng material design, vì vậy hãy thêm một package vào tệpmain.dartđể giúp thực hiện điều đó.

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

Cũng giống như bất kỳ ngôn ngữ lập trình hiện đại nào, bạn có thể import một thư viện/package để sử dụng trong code của mình. Ở đây chúng ta đang import material.dartpackage. Package này chứa code giúp chúng ta tạo một ứng dụng theo kiểu material.

material.dartpackage có một hàm gọi làrunApp.runApplấy một widget và gắn nó vào màn hình. À, vậy widget là gì?

Bạn có thể coi các widget là các khung nhìn (View) hoặc các element giao diện người dùng. Chúng là những thứ bạn nhìn thấy (và một số thứ bạn không thấy) khi bạn chạy ứng dụng của mình trên một thiết bị. Trong Flutter, bạn sẽ sử dụng  rất nhiều widget, vì ý tưởng chính là giao diện người dùng ứng dụng của bạn được tạo hoàn toàn từ widget.

Flutter đi kèm với một bộ widget mạnh mẽ có sẵn như Text và Image. material.dart package mà chúng ta vừa nhập có một số material design widget mà chúng ta sẽ sử dụng trong thời gian ngắn.

Hãy sử dụngrunAppmethod ngay bây giờ để hiển thị “Hello World!” ở giữa màn hình thiết bị. Thay thế nội dung của hàm chính bằng đoạn code bên dưới.

Dart
void main() {
  print('Hello World!');
  
  // Runs the MaterialApp widget
  runApp(new MaterialApp(
    // This is the widget that is displayed first when the application is started normally
    home: new Center(
      // The Text widget is wrapped in a center widget to center it on the screen
      child: new Text('Hello World!'),
    ),
  ));
}

Hãy để mình giải thích một số nội dung mới trong đoạn code trên

  1. new MaterialApp(): Ở đây chúng ta đang tạo một widget object mới có tên làMaterialApp.MaterialAppwidget tạo ra một số thứ hữu ích cần thiết cho một ứng dụng material design.
  2. home: Trong Dart, chúng ta có thể nêu rõ tên của từng tham số trong function/constructor call. Widget được chuyển vào làm tham sốhome(home parameter) được hiển thị đầu tiên khi ứng dụng được khởi động bình thường.
  3. new Center(child: new Text('Hello World!')) : Chúng ra wrap Text widget bên trong Center widget để văn bản được căn giữa trên màn hình. Text widget là một child của Center widget. Và các vật dụng có thể được lồng vào nhau.

Nếu bạn chạy lại code và mở thiết bị của mình, bạn sẽ nhận được một màn hình tốt hơn một chút ngay bây giờ.

Tuyệt vời! Chúng ta có thể hiển thị một văn bản trông xấu xí ở giữa màn hình thiết bị.

Các bước tiếp theo

Bây giờ chúng ta hãy thực hiện một vài bước trước đó. Chúng ta sẽ lấy giá tiền tiền điện tử (cryptocurrency) từ CoinMarketCap API. API trả về một mảng JSON. Đây là phản hồi mẫu từ API:

Dart
[
    {
        "name": "Bitcoin", 
        "price_usd": "11525.7", 
        "percent_change_1h": "-0.18",
        ...
    },
    ...
]

Chúng ta sẽ thực hiện các request đến CoinMarketCap API để lấy dữ liệu và giải mã JSON từ ứng dụng. Chúng ta sẽ phải thêm một vài package mới vào tệpmain.dart.

Dart
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
view raw

Dưới đây là tổng quan về các package mới:

  1. dart:async: Cung cấpFuture mà mình sẽ nói rõ hơn ở phần dưới.
  2. dart:convert: Cung cấpjsonvariable mà chúng ta sẽ sử dụng để decode chuỗi JSON.
  3. package:http/http.dart: Cung cấp hàm mà chúng ta sẽ sử dụng để thực hiện các yêu cầu HTTP GET.

Hãy thêm một hàm mới vào tệpmain.dartđể thực hiện yêu cầu đối với CoinMarketCap API.

Dart
Future<List> getCurrencies() async {
  String apiUrl = 'https://api.coinmarketcap.com/v1/ticker/?limit=50';
  // Make a HTTP GET request to the CoinMarketCap API.
  // Await basically pauses execution until the get() function returns a Response
  http.Response response = await http.get(apiUrl);
  // Using the JSON class to decode the JSON String
  return JSON.decode(response.body);
}

Hãy xem qua code mới

Future<List>: Về cơ bản chúng ta đang nói rằng hàmgetCurrencies()sẽ trả về Listlúc nào đó trong tương lai. Nó sẽ thực hiện một yêu cầu HTTP đến CoinMarketCap API và trả lạiListcác loại tiền tệ khi hoàn tất.

HàmgetCurrencies()không đồng bộ. Nếu bạn có một số kinh nghiệm về JavaScript, bạn có thể coiFuturesPromises. Mình đã tạo những hình ảnh dưới đây để giúp bạn hiểu vềFuturestrong Dart.

Bob gọi hàm không đồng bộ askKateForBalloons() trả về một Future<Balloons>
Bob có thể giữ các Future<Balloons>
Bob có thể biết khi nào thì Future hoàn thành

async and await :

Biểu thức Await cho phép bạn viết code không đồng bộ gần như là code đồng bộ. Hàmhttp.get(url) không đồng bộ, trả về mộtFuture<Response>ngay lập tức khi nó được call. Chúng ta muốn đợiResponseđể có thể decode chuỗi JSON, nhưng chúng ta cũng không muốn sử dụng các ugly callback.

Biểu thức  Await thực hiện http.get(url), sau đó tạm dừng hàm hiện đang chạy (getCurrencies()) cho đến khi kết quả sẵn sàng - nghĩa là cho đến khi Future hoàn thành.

Để sử dụngAwait, code phải nằm trong một hàm được đánh dấu là không đồng bộ. Hàmasynclà một hàm có nội dung được đánh dấu bằng masyncmodifier. Khi bạn call một hàmasync, nó trả về Future ngay lập tức . Phần thân của hàm được lên lịch để thực thi sau đó.

Bạn có thể đọc thêm về asyncAwaittrong Dart tại đây.

http.get(url): Thực hiện yêu cầu HTTP GET tới CoinMarketCap API. Hàm này là hàm bất đồng bộ và trả về Future.

JSON.decode(response.body): Decode chuỗi JSON.

Hãy kiểm tra hàmgetCurrencies()mà chúng ta vừa tạo. Chúng ta thực hiện điều đó bằng cách thực hiện cuộc gọi đến nó trong hàmmaincủa chúng ta và in giá trị trả về vào bảng điều khiển.

Dart
// Since we are using await within the main function, we have to make it asynchronous too
void main() async {
  // Testing the getCurrencies function
  // We wait for the currency data to arrive
  List currencies = await getCurrencies();
  // Before printing it to the Console
  print(currencies);
  
  runApp(new MaterialApp(
    home: new Center(
      child: new Text('Hello World!'),
    ),
  ));
}

Nếu bạn chạy code ở trên, bạn sẽ thấy phản hồi API được in ra bảng điều khiển.

Trong thế giới thực, những điều tồi tệ luôn có thể xảy ra. Ví dụ: bạn có thể không kết nối được với internet, vì vậy yêu cầu tới CoinMarketCap API sẽ không thành công. Đối với hướng dẫn này, chúng ta sẽ giả sử chúng ta đang ở Wakanda.

Trong ứng dụng sản xuất, bạn sẽ phải xử lý lỗi mạng. Bạn làm điều đó bằng cách đưa lệnh gọi HTTP vào khốitry…catch.

Xây dựng UI

Bây giờ chúng ta đã có danh sách các đơn vị tiền tệ, hãy tiếp tục xây dựng giao diện người dùng để hiển thị danh sách đó.

Khi viết ứng dụng Flutter, bạn thường sẽ tạo các class widget mới. Công việc chính của widget là triển khai hàmbuild, hàm này mô tả widget dưới dạng các widget cấp thấp hơn.

Hãy tạo một Widget mới có tên là CryptoListWidget. Thêm code bên dưới vào cuối tệpmain.dartcủa bạn.

Dart
class CryptoListWidget extends StatelessWidget {
  
  // This is a list of material colors. Feel free to add more colors, it won't break the code
  final List<MaterialColor> _colors = [Colors.blue, Colors.indigo, Colors.red];
  // The underscore before a variable name marks it as a private variable
  final List _currencies;

  // This is a constructor in Dart. We are assigning the value passed to the constructor
  // to the _currencies variable
  CryptoListWidget(this._currencies);

  @override
  Widget build(BuildContext context) {
    // Build describes the widget in terms of other, lower-level widgets.
    return new Text('Hello World!');
  }

}

Hãy xem qua code mới ở trên:

  1. StatelessWidget: Bạn thường sẽ tạo các Widget là subclass của StatelessWidget hoặc StatefulWidget, tùy thuộc vào việc widget của bạn quản lý state nào. Chúng mình đang sử dụng StatelessWidget vì chúng mình đã có dữ liệu của mình và chúng mình sẽ không cập nhật dữ liệu đó trong hướng dẫn này.
  2. final List<MaterialColor> _colors: Các variable trong StatelessWidget phải là cuối cùng (có nghĩa là chúng không thay đổi). Ở đây chúng mình đang khai báo một variable cuối cùng chứa danh sách các MaterialColors. Dấu gạch dưới (_) trước tên variable làm cho nó ở chế độ riêng tư (không thể truy cập từ các class khác).
  3. CryptoListWidget(this._currencies): Đây là constructor cho widget của chúng ta. Nó chỉ định danh sách các đơn vị tiền tệ mà chúng ta chuyển vào constructor cho _currencies variable.
  4. buildfunction: Build method trả về một Widget cấp thấp hơn (Text) mô tả nó sẽ trông như thế nào.

Hãy thay thế Text widget trong hàmbuildở trên bằng một widget mới có tên làScaffold.Scaffoldwidget triển khai cấu trúc bố cục hình ảnh material design cơ bản. Thay thế hàmbuildở trên bằng hàm bên dưới.

Dart
@override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: _buildBody(),
      backgroundColor: Colors.blue,
    );
  }

Scaffold cung cấp các API để hiển thị drawers, float action button, bottom bar, snack bar, v.v. Chúng ta sẽ thêm một floating action button sau.

Bạn sẽ nhận được cảnh báo rằng_buildBody()không được định nghĩa cho classCryptoListWidget. Hãy tiếp tục tạo hàm _buildBody()bên trong class CryptoListWidget.

Dart
Widget _buildBody() {
    return new Container(
      // A top margin of 56.0. A left and right margin of 8.0. And a bottom margin of 0.0.
      margin: const EdgeInsets.fromLTRB(8.0, 56.0, 8.0, 0.0),
      child: new Column(
        // A column widget can have several widgets that are placed in a top down fashion
        children: <Widget>[
          _getAppTitleWidget(),
          _getListViewWidget()
        ],
      ),
    );
  }

Cú pháp ở đây bạn khá là quen thuộc. Chúng ta đang sử dụng hai Widget mới:

  1. Containerwidget:Containerwidget chỉ là một container :) (đối với các widget khác).
  2. Column widget:Columnwidget bố trí danh sách các child widget theo hướng dọc. Nó có một tham số được gọi làchildrenđể lấy danh sách các widget.

Hãy tạo hai hàm mà chúng ta đã gọi trong hàm_buildBody(). Cái đầu tiên là_getAppTitleWidget().

Dart
Widget _getAppTitleWidget() {
    return new Text(
      'Cryptocurrencies',
      style: new TextStyle(
          color: Colors.white,
          fontWeight: FontWeight.bold,
          fontSize: 24.0),
    );
  }

Hàm này trả về một Text widget thông thường có kiểu làm cho nó đậm và trắng với kích thước phông chữ là 24.0. Văn bản sẽ trông như thế này khi chúng ta chạy ứng dụng.

Chúng ta chưa thể chạy ứng dụng vì chúng ta chưa tạo hàm khác có tên_getListViewWidget(). Hãy nhanh chóng tạo nó bằng cách sử dụng code bên dưới.

Dart
Widget _getListViewWidget() {
    // We want the ListView to have the flexibility to expand to fill the
    // available space in the vertical axis
    return new Flexible(
        child: new ListView.builder(
            // The number of items to show
            itemCount: _currencies.length,
            // Callback that should return ListView children
            // The index parameter = 0...(itemCount-1)
            itemBuilder: (context, index) {
              // Get the currency at this position
              final Map currency = _currencies[index];

              // Get the icon color. Since x mod y, will always be less than y,
              // this will be within bounds
              final MaterialColor color = _colors[index % _colors.length];

              return _getListItemWidget(currency, color);
            }));
  }

_getListViewWidget()trả vềListViewwidget được wrap trongFlexiblewidget. Chúng ta sử dụngListView.builderđể dễ dàng tạo danh sách. Chúng ta chuyển vàoitemCountcho builder biết có bao nhiêu đơn vị tiền tệ để hiển thị.

itemBuildercallback sẽ được call cho từng item và bạn phải trả lại một widget mới. Trong đoạn code trên, chúng ta đang call một hàm có tên_getListItemWidget()trả về một Widget.

Trước khi chúng ta tạo hàm_getListItemWidget(), hãy nhanh chóng tạo các element riêng lẻ cho ListView item widget. Chúng ta muốn mỗi mục trong ListView trông như thế này:

Vì vậy, chúng ta có ba widget chính:

  1. Round icon widget có ký tự đầu tiên của tên đơn vị tiền tệ
  2. Một text widget có tên đơn vị tiền tệ
  3. Một text widget với giá và phần trăm thay đổi trong 1 giờ.

Hãy tạo các widget. Vì lợi ích đơn giản, mình đã tạo một hàm cho mỗi cái trong số chúng. Hàm đầu tiên được gọi là_getLeadingWidget()trả về biểu tượng tròn có văn bản.

Dart
CircleAvatar _getLeadingWidget(String currencyName, MaterialColor color) {
    return new CircleAvatar(
      backgroundColor: color,
      child: new Text(currencyName[0]),
    );
  }

Widget sẽ trông như thế này:

Hàm thứ hai được gọi là_getTitleWidgettrả vềTextwidget cho tên đơn vị tiền tệ.

Hàm thứ ba được gọi là _getSubtitleWidget() trả vềTextwidget cho giá tiền hiện tại và phần trăm thay đổi trong 1 giờ.

Dart
Text _getTitleWidget(String currencyName) {
    return new Text(
      currencyName,
      style: new TextStyle(fontWeight: FontWeight.bold),
    );
  }

  Text _getSubtitleWidget(String priceUsd, String percentChange1h) {
    return new Text('\$$priceUsd\n1 hour: $percentChange1h%');
  }

Cả hai widget này sẽ trông như thế này:

Hãy wrap tất cả ba widget trong một thứ gọi là ListTile widget. ListTile widget dựa trên tài liệu Material Design List. Nó hiển thị tất cả ba widget theo kiểu này.

Chúng ta sẽ tạo một hàm mới có tên _getListTile trả về một ListTile widget.

Dart
ListTile _getListTile(Map currency, MaterialColor color) {
    return new ListTile(
      leading: _getLeadingWidget(currency['name'], color),
      title: _getTitleWidget(currency['name']),
      subtitle: _getSubtitleWidget(
          currency['price_usd'], currency['percent_change_1h']),
      isThreeLine: true,
    );
  }

Cuối cùng, hãy tạo_getListItemWidget(). Nó sẽ trả về mộtContainerwidget có top padding là 5.0 và có Card widget child. Card widget cóListTileđược_getListTiletrả về khi là child của nó.

Dart
Container _getListItemWidget(Map currency, MaterialColor color) {
    // Returns a container widget that has a card child and a top margin of 5.0
    return new Container(
      margin: const EdgeInsets.only(top: 5.0),
      child: new Card(
        child: _getListTile(currency, color),
      ),
    );
  }

Widget sẽ trông như thế này.

Chúng ta đã hoàn thànhCryptoListWidgetcủa mình thành công. Nhưng chúng ta phải cập nhật hàmmainđể hiển thị widget mới tạo thay vìText widget.

Dart
void main() async {
  // Bad practice alert :). You should ideally show the UI, and probably a progress view,
  // then when the requests completes, update the UI to show the data.
  List currencies = await getCurrencies();
  print(currencies);
  
  runApp(new MaterialApp(
    home: new CryptoListWidget(currencies),
  ));
}

Bạn có thể nhấn lại nút run. Nếu mọi thứ hoạt động tốt và bạn được kết nối với Internet, bạn sẽ có một màn hình trông như thế này.

Thật tuyệt phải không?

Ứng dụng bạn thấy sẽ hơi khác so với ảnh chụp màn hình ở trên:

  1. Nó không có nút tác vụ nổi ở dưới cùng bên phải.
  2. Màu văn bản của phần trăm thay đổi trong 1 giờ là màu đen.

Mình quyết định không đưa chúng vào hướng dẫn. Nhưng bạn có thể kiểm tra kho lưu trữ Github của dự án để xem mình đã có thể làm được nó như thế nào.

Ứng dụng hoàn thành có thể được tải xuống tại đây.

Cảm ơn bạn đã đọc và hy vọng bạn cũng thích Flutter như mình.

Bài viết được lược dịch từ Elvis Chidera.

Bài viết liên quan

Lập trình backend expressjs

xây dựng hệ thống microservices
  • Kiến trúc Hexagonal và ứng dụngal font-
  • TypeScript: OOP và nguyên lý SOLIDal font-
  • Event-Driven Architecture, Queue & PubSubal font-
  • Basic scalable System Designal font-

Đăng ký nhận thông báo

Đừng bỏ lỡ những bài viết thú vị từ 200Lab