Flutter - Hướng dẫn nhanh

Nói chung, phát triển một ứng dụng di động là một nhiệm vụ phức tạp và đầy thách thức. Có rất nhiều khuôn khổ có sẵn để phát triển một ứng dụng di động. Android cung cấp một khuôn khổ gốc dựa trên ngôn ngữ Java và iOS cung cấp một khuôn khổ gốc dựa trên ngôn ngữ Objective-C / Swift.

Tuy nhiên, để phát triển một ứng dụng hỗ trợ cả hai hệ điều hành, chúng ta cần viết mã bằng hai ngôn ngữ khác nhau bằng hai khung công tác khác nhau. Để giúp khắc phục sự phức tạp này, có các khung di động hỗ trợ cả hai hệ điều hành. Các khuôn khổ này bao gồm từ khuôn khổ ứng dụng di động kết hợp dựa trên HTML đơn giản (sử dụng HTML cho Giao diện người dùng và JavaScript cho logic ứng dụng) đến khuôn khổ ngôn ngữ cụ thể phức tạp (thực hiện công việc chuyển đổi mã sang mã gốc). Bất kể đơn giản hay phức tạp, các framework này luôn tồn tại nhiều nhược điểm, một trong những nhược điểm chính là hiệu suất chậm.

Trong trường hợp này, Flutter - một khuôn khổ đơn giản và hiệu suất cao dựa trên ngôn ngữ Dart, cung cấp hiệu suất cao bằng cách hiển thị giao diện người dùng trực tiếp trong khung của hệ điều hành thay vì thông qua khuôn khổ gốc.

Flutter cũng cung cấp nhiều tiện ích con (UI) sẵn sàng sử dụng để tạo ra một ứng dụng hiện đại. Các widget này được tối ưu hóa cho môi trường di động và việc thiết kế ứng dụng bằng widget cũng đơn giản như thiết kế HTML.

Cụ thể, ứng dụng Flutter tự nó là một widget. Các widget Flutter cũng hỗ trợ các hình ảnh động và cử chỉ. Logic ứng dụng dựa trên lập trình phản ứng. Widget có thể có trạng thái tùy chọn. Bằng cách thay đổi trạng thái của tiện ích, Flutter sẽ tự động (lập trình phản ứng) so sánh trạng thái của tiện ích (cũ và mới) và hiển thị tiện ích chỉ với những thay đổi cần thiết thay vì hiển thị lại toàn bộ tiện ích.

Chúng ta sẽ thảo luận về kiến ​​trúc hoàn chỉnh trong các chương tới.

Các tính năng của Flutter

Flutter framework cung cấp các tính năng sau cho các nhà phát triển:

  • Khung hiện đại và phản ứng.

  • Sử dụng ngôn ngữ lập trình Dart và nó rất dễ học.

  • Phát triển nhanh chóng.

  • Giao diện người dùng đẹp và linh hoạt.

  • Danh mục phụ tùng khổng lồ.

  • Chạy cùng một giao diện người dùng cho nhiều nền tảng.

  • Ứng dụng hiệu suất cao.

Ưu điểm của Flutter

Flutter đi kèm với các widget đẹp và có thể tùy chỉnh để có hiệu suất cao và ứng dụng di động nổi bật. Nó đáp ứng tất cả các nhu cầu và yêu cầu tùy chỉnh. Bên cạnh những điều này, Flutter còn cung cấp nhiều ưu điểm khác như được đề cập bên dưới -

  • Dart có một kho phần mềm lớn cho phép bạn mở rộng các khả năng của ứng dụng của mình.

  • Các nhà phát triển chỉ cần viết một cơ sở mã duy nhất cho cả hai ứng dụng (cả nền tảng Android và iOS). Flutter cũng có thể được mở rộng sang nền tảng khác trong tương lai.

  • Flutter cần ít thử nghiệm hơn. Do cơ sở mã duy nhất của nó, nó là đủ nếu chúng tôi viết các thử nghiệm tự động một lần cho cả hai nền tảng.

  • Sự đơn giản của Flutter khiến nó trở thành một ứng cử viên sáng giá để phát triển nhanh chóng. Khả năng tùy biến và khả năng mở rộng của nó làm cho nó trở nên mạnh mẽ hơn.

  • Với Flutter, các nhà phát triển có toàn quyền kiểm soát các widget và bố cục của nó.

  • Flutter cung cấp các công cụ tuyệt vời dành cho nhà phát triển, với khả năng tải lại nóng đáng kinh ngạc.

Nhược điểm của Flutter

Mặc dù có nhiều ưu điểm, nhưng Flagship có những nhược điểm sau:

  • Vì nó được mã hóa bằng ngôn ngữ Dart, nhà phát triển cần học ngôn ngữ mới (mặc dù nó rất dễ học).

  • Khung công tác hiện đại cố gắng tách biệt logic và giao diện người dùng nhiều nhất có thể, nhưng trong Flutter, giao diện người dùng và logic được trộn lẫn với nhau. Chúng tôi có thể khắc phục điều này bằng cách sử dụng mã hóa thông minh và sử dụng mô-đun cấp cao để phân tách giao diện người dùng và logic.

  • Flutter là một khuôn khổ khác để tạo ứng dụng di động. Các nhà phát triển đang gặp khó khăn trong việc lựa chọn các công cụ phát triển phù hợp trong phân khúc đông dân cư.

Chương này sẽ hướng dẫn bạn cài đặt Flutter trên máy tính cục bộ một cách chi tiết.

Cài đặt trong Windows

Trong phần này, chúng ta hãy xem cách cài đặt Flutter SDK và yêu cầu của nó trong hệ thống windows.

Step 1 - Đi tới URL,https://flutter.dev/docs/get-started/install/windowsvà tải xuống Flutter SDK mới nhất. Kể từ tháng 4 năm 2019, phiên bản là 1.2.1 và tệp là flay_windows_v1.2.1-stable.zip.

Step 2 - Giải nén kho lưu trữ zip trong một thư mục, nói C: \ flashing \

Step 3 - Cập nhật đường dẫn hệ thống để bao gồm thư mục bin rung.

Step 4 - Flutter cung cấp một công cụ, bác sĩ rung để kiểm tra xem tất cả các yêu cầu của sự phát triển của rung có được đáp ứng hay không.

flutter doctor

Step 5 - Chạy lệnh trên sẽ phân tích hệ thống và hiển thị báo cáo của nó như hình dưới đây -

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.2.1, on Microsoft Windows [Version
10.0.17134.706], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version
28.0.3)
[√] Android Studio (version 3.2)
[√] VS Code, 64-bit edition (version 1.29.1)
[!] Connected device
! No devices available
! Doctor found issues in 1 category.

Báo cáo nói rằng tất cả các công cụ phát triển đều có sẵn nhưng thiết bị không được kết nối. Chúng tôi có thể khắc phục sự cố này bằng cách kết nối thiết bị Android qua USB hoặc khởi động trình giả lập Android.

Step 6 - Cài đặt Android SDK mới nhất, nếu được bác sĩ báo cáo

Step 7 - Cài đặt Android Studio mới nhất, nếu bị bác sĩ báo cáo

Step 8 - Khởi động trình giả lập Android hoặc kết nối thiết bị Android thực với hệ thống.

Step 9- Cài đặt plugin Flutter and Dart cho Android Studio. Nó cung cấp mẫu khởi động để tạo ứng dụng Flutter mới, một tùy chọn để chạy và gỡ lỗi ứng dụng Flutter trong chính Android studio, v.v.

  • Mở Android Studio.

  • Nhấp vào Tệp → Cài đặt → Plugin.

  • Chọn plugin Flutter và nhấp vào Cài đặt.

  • Nhấp vào Có khi được nhắc cài đặt plugin Dart.

  • Khởi động lại Android studio.

Cài đặt trong MacOS

Để cài đặt Flutter trên MacOS, bạn sẽ phải làm theo các bước sau:

Step 1 - Đi tới URL,https://flutter.dev/docs/get-started/install/macosvà tải xuống Flutter SDK mới nhất. Kể từ tháng 4 năm 2019, phiên bản là 1.2.1 và tệp là flay_macos_v1.2.1- stable.zip.

Step 2 - Giải nén kho lưu trữ zip trong một thư mục, nói / path / to / Flaming

Step 3 - Cập nhật đường dẫn hệ thống để đưa vào thư mục flashing bin (trong tệp ~ / .bashrc).

> export PATH = "$PATH:/path/to/flutter/bin"

Step 4 - Bật đường dẫn được cập nhật trong phiên hiện tại bằng lệnh dưới đây và sau đó xác minh nó.

source ~/.bashrc
source $HOME/.bash_profile
echo $PATH

Flutter cung cấp một công cụ, bác sĩ rung để kiểm tra xem tất cả các yêu cầu của sự phát triển rung có được đáp ứng hay không. Nó tương tự như đối tác Windows.

Step 5 - Cài đặt XCode mới nhất, nếu được bác sĩ báo cáo

Step 6 - Cài đặt Android SDK mới nhất, nếu được bác sĩ báo cáo

Step 7 - Cài đặt Android Studio mới nhất, nếu bị bác sĩ báo cáo

Step 8 - Khởi động trình giả lập android hoặc kết nối thiết bị android thực với hệ thống để phát triển ứng dụng android.

Step 9 - Mở trình giả lập iOS hoặc kết nối thiết bị iPhone thực với hệ thống để phát triển ứng dụng iOS.

Step 10- Cài đặt plugin Flutter and Dart cho Android Studio. Nó cung cấp mẫu khởi động để tạo một ứng dụng Flutter mới, tùy chọn chạy và gỡ lỗi ứng dụng Flutter trong chính Android studio, v.v.

  • Mở Android Studio

  • Nhấp chuột Preferences → Plugins

  • Chọn plugin Flutter và nhấp vào Cài đặt

  • Nhấp vào Có khi được nhắc cài đặt plugin Dart.

  • Khởi động lại Android studio.

Trong chương này, chúng ta hãy tạo một ứng dụng Flutter đơn giản để hiểu cơ bản về cách tạo một ứng dụng Flutter trong Android Studio.

Step 1 - Mở Android Studio

Step 2- Tạo dự án Flutter. Đối với điều này, hãy nhấp vàoFile → New → New Flutter Project

Step 3- Chọn Ứng dụng Flutter. Đối với điều này, hãy chọnFlutter Application và bấm vào Next.

Step 4 - Cấu hình ứng dụng như bên dưới và nhấp vào Next.

  • Tên dự án: hello_app

  • Đường dẫn SDK Flutter: <path_to_flutter_sdk>

  • Địa điểm dự án: <path_to_project_folder>

  • Sự miêu tả: Flutter based hello world application

Step 5 - Cấu hình dự án.

Đặt miền công ty là flutterapp.tutorialspoint.com và bấm vào Finish.

Step 6 - Nhập tên miền Công ty.

Android Studio tạo ra một ứng dụng rung hoàn toàn hoạt động với chức năng tối thiểu. Hãy để chúng tôi kiểm tra cấu trúc của ứng dụng và sau đó, thay đổi mã để thực hiện nhiệm vụ của chúng tôi.

Cấu trúc của ứng dụng và mục đích của nó như sau:

Các thành phần khác nhau của cấu trúc của ứng dụng được giải thích ở đây -

  • android - Mã nguồn được tạo tự động để tạo ứng dụng Android

  • ios - Mã nguồn được tạo tự động để tạo ứng dụng ios

  • lib - Thư mục chính chứa mã Dart được viết bằng khung làm việc

  • ib/main.dart - Điểm vào của ứng dụng Flutter

  • test - Thư mục chứa mã Dart để kiểm tra ứng dụng rung

  • test/widget_test.dart - Mã mẫu

  • .gitignore - Tệp điều khiển phiên bản Git

  • .metadata - tự động tạo ra bởi các công cụ rung

  • .packages - tự động tạo ra để theo dõi các gói rung

  • .iml - tệp dự án được Android studio sử dụng

  • pubspec.yaml - Được sử dụng bởi Pub, Trình quản lý gói Flutter

  • pubspec.lock - Được tạo tự động bởi trình quản lý gói Flutter, Pub

  • README.md - Tệp mô tả dự án được viết ở định dạng Markdown

Step 7- Thay thế mã phi tiêu trong tệp lib / main.dart bằng mã bên dưới -

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Hello World Demo Application',
         theme: ThemeData(
            primarySwatch: Colors.blue,
         ),
         home: MyHomePage(title: 'Home page'),
      );
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;

   @override
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(
            title: Text(this.title),
         ),
         body: Center(
            child:
            Text(
               'Hello World',
            )
         ),
      );
   }
}

Hãy để chúng tôi hiểu từng dòng mã phi tiêu.

  • Line 1- nhập khẩu các gói, vật liệu rung . Vật liệu là một gói rung để tạo giao diện người dùng theo nguyên tắc thiết kế Vật liệu do Android chỉ định.

  • Line 3- Đây là điểm vào của ứng dụng Flutter. Gọi hàm runApp và chuyển cho nó một đối tượng của lớp MyApp . Mục đích của hàm runApp là đính kèm tiện ích đã cho vào màn hình.

  • Line 5-17- Widget được sử dụng để tạo giao diện người dùng trong khung làm việc. StatelessWidget là một widget, không duy trì bất kỳ trạng thái nào của widget. MyApp mở rộng StatelessWidget và ghi đè phương thức xây dựng của nó . Mục đích của phương pháp xây dựng là tạo một phần giao diện người dùng của ứng dụng. Ở đây, phương pháp xây dựng sử dụng MaterialApp , một tiện ích để tạo giao diện người dùng cấp gốc của ứng dụng. Nó có ba thuộc tính - tiêu đề, chủ đềtrang chủ .

    • title là tiêu đề của ứng dụng

    • chủ đề là chủ đề của tiện ích con. Ở đây, chúng tôi đặt màu xanh lam làm màu tổng thể của ứng dụng bằng cách sử dụng lớp ThemeData và thuộc tính của nó, primarySwatch .

    • home là giao diện người dùng bên trong của ứng dụng, mà chúng tôi đặt một tiện ích khác, MyHomePage

  • Line 19 - 38- MyHomePage giống như MyApp ngoại trừ nó trả về Scaffold Widget. Scaffold là một tiện ích cấp cao nhất bên cạnh tiện ích MaterialApp được sử dụng để tạo thiết kế material design tuân theo giao diện người dùng. Nó có hai thuộc tính quan trọng, appBar để hiển thị phần đầu của ứng dụng và phần thân để hiển thị nội dung thực tế của ứng dụng. AppBar là một tiện ích khác để hiển thị tiêu đề của ứng dụng và chúng tôi đã sử dụng nó trong thuộc tính appBar . Trong thuộc tính body , chúng tôi đã sử dụng trung tâm widget, trung tâm nó là widget con. Văn bản là tiện ích cuối cùng và bên trong nhất để hiển thị văn bản và nó được hiển thị ở giữa màn hình.

Step 8 - Bây giờ, chạy ứng dụng bằng cách sử dụng, Run → Run main.dart

Step 9 - Cuối cùng, kết quả của ứng dụng như sau:

Trong chương này, chúng ta hãy thảo luận về kiến ​​trúc của Flutter framework.

Vật dụng

Khái niệm cốt lõi của khung Flutter là In Flutter, Everything is a widget. Widget về cơ bản là các thành phần giao diện người dùng được sử dụng để tạo giao diện người dùng của ứng dụng.

Trong Flutter , ứng dụng tự nó là một widget. Ứng dụng là tiện ích con cấp cao nhất và giao diện người dùng của nó được xây dựng bằng cách sử dụng một hoặc nhiều tiện ích con (widget), lại được xây dựng bằng cách sử dụng các tiện ích con của nó. Điều nàycomposability tính năng này giúp chúng tôi tạo giao diện người dùng ở mọi mức độ phức tạp.

Ví dụ: cấu trúc phân cấp tiện ích con của ứng dụng hello world (được tạo trong chương trước) như được chỉ định trong sơ đồ sau:

Dưới đây là những điểm đáng chú ý sau:

  • MyApp là widget do người dùng tạo và nó được xây dựng bằng widget gốc của Flutter, MaterialApp .

  • MaterialApp có một thuộc tính trang chủ để chỉ định giao diện người dùng của trang chủ, đây lại là một tiện ích do người dùng tạo, MyHomePage .

  • MyHomePage được xây dựng bằng một widget gốc khác, Scaffold

  • Scaffold có hai thuộc tính - bodyappBar

  • body được sử dụng để chỉ định giao diện người dùng chính của nó và appBar được sử dụng để chỉ định giao diện người dùng tiêu đề của nó

  • Giao diện người dùng Header được xây dựng bằng tiện ích con gốc rung, Giao diện người dùng AppBarBody được xây dựng bằng tiện ích con Trung tâm .

  • Các Trung tâm phụ tùng có một tài sản, trẻ em , trong đó đề cập các nội dung thực tế và nó là xây dựng sử dụng chữ phụ tùng

Cử chỉ

Các widget Flutter hỗ trợ tương tác thông qua một widget đặc biệt, GestureDetector . GestureDetector là một tiện ích con vô hình có khả năng nắm bắt các tương tác của người dùng như chạm, kéo, v.v. của tiện ích con của nó. Nhiều widget gốc của Flutter hỗ trợ tương tác thông qua việc sử dụng GestureDetector . Chúng tôi cũng có thể kết hợp tính năng tương tác vào tiện ích hiện có bằng cách soạn nó với tiện ích GestureDetector . Chúng ta sẽ tìm hiểu các cử chỉ riêng trong các chương sắp tới.

Khái niệm về Nhà nước

Các widget Flutter hỗ trợ bảo trì State bằng cách cung cấp một widget đặc biệt, StatefulWidget . Widget cần được bắt nguồn từ widget StatefulWidget để hỗ trợ duy trì trạng thái và tất cả các widget khác phải bắt nguồn từ StatefulWidget . Các widget Flutter làreactivetrong bản địa. Điều này tương tự như reactjs và StatefulWidget sẽ được tự động hiển thị bất cứ khi nào trạng thái bên trong của nó bị thay đổi. Kết xuất được tối ưu hóa bằng cách tìm ra sự khác biệt giữa giao diện người dùng tiện ích con cũ và mới và chỉ hiển thị những thay đổi cần thiết

Lớp

Khái niệm quan trọng nhất của Flutter framework là framework được nhóm thành nhiều loại về mức độ phức tạp và được sắp xếp rõ ràng theo các lớp giảm dần độ phức tạp. Một lớp được xây dựng bằng cách sử dụng lớp cấp tiếp theo ngay lập tức của nó. Lớp trên cùng là widget dành riêng cho AndroidiOS . Lớp tiếp theo có tất cả các widget gốc. Lớp tiếp theo là Lớp kết xuất, là thành phần trình kết xuất mức thấp và hiển thị mọi thứ trong ứng dụng Flagship. Các lớp đi xuống mã cụ thể của nền tảng cốt lõi

Tổng quan chung về một lớp trong Flutter được chỉ định trong sơ đồ dưới đây:

Những điểm sau đây tóm tắt kiến ​​trúc của Flutter:

  • Trong Flutter, mọi thứ đều là một widget và một widget phức tạp bao gồm các widget đã có sẵn.

  • Các tính năng tương tác có thể được kết hợp bất cứ khi nào cần thiết bằng tiện ích GestureDetector .

  • Trạng thái của tiện ích có thể được duy trì bất cứ khi nào cần thiết bằng tiện ích StatefulWidget .

  • Flutter cung cấp thiết kế phân lớp để có thể lập trình bất kỳ lớp nào tùy thuộc vào mức độ phức tạp của nhiệm vụ.

Chúng ta sẽ thảo luận chi tiết tất cả các khái niệm này trong các chương sắp tới.

Dart là một ngôn ngữ lập trình đa năng mã nguồn mở. Nó ban đầu được phát triển bởi Google. Dart là một ngôn ngữ hướng đối tượng với cú pháp kiểu C. Nó hỗ trợ các khái niệm lập trình như giao diện, lớp, không giống như các ngôn ngữ lập trình khác Dart không hỗ trợ mảng. Bộ sưu tập Dart có thể được sử dụng để sao chép cấu trúc dữ liệu như mảng, generic và kiểu gõ tùy chọn.

Đoạn mã sau đây cho thấy một chương trình Dart đơn giản:

void main() {
   print("Dart language is easy to learn");
}

Biến và kiểu dữ liệu

Biến được đặt tên là vị trí lưu trữ và Kiểu dữ liệu chỉ đơn giản là đề cập đến loại và kích thước của dữ liệu được liên kết với các biến và hàm.

Dart sử dụng từ khóa var để khai báo biến. Cú pháp của var được định nghĩa bên dưới,

var name = 'Dart';

Các thứcconst từ khóa được sử dụng để khai báo hằng. Chúng được định nghĩa như sau:

void main() {
   final a = 12;
   const pi = 3.14;
   print(a);
   print(pi);
}

Ngôn ngữ Dart hỗ trợ các kiểu dữ liệu sau:

  • Numbers - Nó được sử dụng để biểu diễn các ký tự số - Integer và Double.

  • Strings- Nó đại diện cho một chuỗi các ký tự. Giá trị chuỗi được chỉ định trong dấu ngoặc kép hoặc đơn.

  • Booleans- Dart sử dụng từ khóa bool để biểu diễn các giá trị Boolean - true và false.

  • Lists and Maps- Nó được sử dụng để đại diện cho một tập hợp các đối tượng. Một danh sách đơn giản có thể được định nghĩa như sau -.

void main() {
   var list = [1,2,3,4,5];
   print(list);
}

Danh sách hiển thị ở trên tạo ra danh sách [1,2,3,4,5].

Bản đồ có thể được xác định như được hiển thị ở đây -

void main() {
   var mapping = {'id': 1,'name':'Dart'};
   print(mapping);
}
  • Dynamic- Nếu kiểu biến không được xác định, thì kiểu mặc định của nó là động. Ví dụ sau minh họa biến kiểu động:

void main() {
   dynamic name = "Dart";
   print(name);
}

Ra quyết định và vòng lặp

Khối ra quyết định đánh giá một điều kiện trước khi các lệnh được thực thi. Dart hỗ trợ các câu lệnh If, If..else và switch.

Vòng lặp được sử dụng để lặp lại một khối mã cho đến khi một điều kiện cụ thể được đáp ứng. Dart hỗ trợ các vòng lặp for, for..in, while và do.. while.

Hãy để chúng tôi hiểu một ví dụ đơn giản về việc sử dụng các câu lệnh và vòng lặp điều khiển -

void main() {
   for( var i = 1 ; i <= 10; i++ ) {
      if(i%2==0) {
         print(i);
      }
   }
}

Đoạn mã trên in các số chẵn từ 1 đến 10.

Chức năng

Hàm là một nhóm các câu lệnh cùng thực hiện một nhiệm vụ cụ thể. Chúng ta hãy xem xét một chức năng đơn giản trong Dart như được hiển thị ở đây -

void main() {
   add(3,4);
}
void add(int a,int b) {
   int c;
   c = a+b;
   print(c);
}

Hàm trên thêm hai giá trị và tạo ra 7 làm đầu ra.

Lập trình hướng đối tượng

Dart là một ngôn ngữ hướng đối tượng. Nó hỗ trợ các tính năng lập trình hướng đối tượng như lớp, giao diện, v.v.

Một lớp là một bản thiết kế để tạo các đối tượng. Một định nghĩa lớp bao gồm những điều sau:

  • Fields
  • Getters và setters
  • Constructors
  • Functions

Bây giờ, chúng ta hãy tạo một lớp đơn giản bằng cách sử dụng các định nghĩa trên -

class Employee {
   String name;
   
   //getter method
   String get emp_name {
      return name;
   }
   //setter method
   void set emp_name(String name) {
      this.name = name;
   }
   //function definition
   void result() {
      print(name);
   }
}
void main() {
   //object creation
   Employee emp = new Employee();
   emp.name = "employee1";
   emp.result(); //function call
}

Như chúng ta đã học ở chương trước, widget là tất cả mọi thứ trong Flutter framework. Chúng ta đã học cách tạo widget mới trong các chương trước.

Trong chương này, chúng ta hãy hiểu khái niệm thực tế đằng sau việc tạo ra các widget và các loại widget khác nhau có sẵn trong Flutter framework.

Hãy để chúng tôi kiểm tra widget MyHomePage của ứng dụng Hello World . Mã cho mục đích này như dưới đây:

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   
   final String title; 
   @override 
   Widget build(BuildContext context) {
      return Scaffold( 
         appBar: AppBar(title: Text(this.title), ), 
         body: Center(child: Text( 'Hello World',)),
      );
   }
}

Ở đây, chúng tôi đã tạo một widget mới bằng cách mở rộng StatelessWidget .

Lưu ý rằng StatelessWidget chỉ yêu cầu một bản dựng phương thức duy nhất được triển khai trong lớp dẫn xuất của nó. Các xây dựng phương pháp được môi trường bối cảnh cần thiết để xây dựng các widget thông qua BuildContext tham số và trả về widget nó được xây dựng.

Trong đoạn mã, chúng tôi đã sử dụng title làm một trong các đối số của hàm tạo và cũng sử dụng Key làm đối số khác. Các tiêu đề được sử dụng để hiển thị tiêu đề và chính được sử dụng để xác định các widget trong việc xây dựng môi trường.

Ở đây, phương thức xây dựng gọi phương thức xây dựng của Scaffold , lần lượt gọi phương thức xây dựng của AppBarCenter để xây dựng giao diện người dùng của nó.

Cuối cùng, phương thức xây dựng Trung tâm gọi Phương thức xây dựng văn bản .

Để hiểu rõ hơn, trình bày trực quan của điều tương tự được đưa ra dưới đây:

Hình ảnh hóa bản dựng tiện ích

Trong Flutter , các widget có thể được nhóm thành nhiều danh mục dựa trên các tính năng của chúng, như được liệt kê bên dưới:

  • Các tiện ích cụ thể cho nền tảng
  • Bố cục widget
  • Các vật dụng bảo trì trạng thái
  • Các widget cơ bản / độc lập với nền tảng

Hãy để chúng tôi thảo luận chi tiết về từng người trong số họ bây giờ.

Các tiện ích cụ thể cho nền tảng

Flutter có các widget dành riêng cho một nền tảng cụ thể - Android hoặc iOS.

Các widget cụ thể của Android được thiết kế theo nguyên tắc thiết kế Material design của hệ điều hành Android. Các tiện ích cụ thể của Android được gọi là tiện ích Vật liệu .

Các widget cụ thể của iOS được Apple thiết kế theo Nguyên tắc về giao diện con người và chúng được gọi là widget của Cupertino .

Một số vật liệu vật liệu được sử dụng nhiều nhất như sau:

  • Scaffold
  • AppBar
  • BottomNavigationBar
  • TabBar
  • TabBarView
  • ListTile
  • RaisedButton
  • FloatingActionButton
  • FlatButton
  • IconButton
  • DropdownButton
  • PopupMenuButton
  • ButtonBar
  • TextField
  • Checkbox
  • Radio
  • Switch
  • Slider
  • Bộ chọn ngày & giờ
  • SimpleDialog
  • AlertDialog

Một số vật dụng Cupertino được sử dụng nhiều nhất như sau:

  • CupertinoButton
  • CupertinoPicker
  • CupertinoDatePicker
  • CupertinoTimerPicker
  • CupertinoNavigationBar
  • CupertinoTabBar
  • CupertinoTabScaffold
  • CupertinoTabView
  • CupertinoTextField
  • CupertinoDialog
  • CupertinoDialogAction
  • CupertinoFullscreenDialogTransition
  • CupertinoPageScaffold
  • CupertinoPageTransition
  • CupertinoActionSheet
  • CupertinoActivityIndicator
  • CupertinoAlertDialog
  • CupertinoPopupSurface
  • CupertinoSlider

Bố cục widget

Trong Flutter, một widget có thể được tạo bằng cách soạn một hoặc nhiều widget. Để tổng hợp nhiều widget thành một widget duy nhất, Flutter cung cấp một số lượng lớn các widget với tính năng bố cục. Ví dụ: tiện ích con có thể được căn giữa bằng tiện ích Trung tâm .

Một số tiện ích bố cục phổ biến như sau:

  • Container- Một hình hộp chữ nhật được trang trí bằng các vật dụng BoxDecoration với nền, đường viền và bóng.

  • Center - Căn giữa widget con của nó.

  • Row - Sắp xếp các con của nó theo chiều ngang.

  • Column - Sắp xếp các con của nó theo chiều dọc.

  • Stack - Sắp xếp cái này lên trên cái khác.

Chúng tôi sẽ kiểm tra chi tiết các widget bố cục trong chương Giới thiệu sắp tới về các widget bố cục .

Các vật dụng bảo trì trạng thái

Trong Flutter, tất cả các widget đều có nguồn gốc từ StatelessWidget hoặc StatefulWidget .

Widget có nguồn gốc từ StatelessWidget không có bất kỳ thông tin trạng thái nào nhưng nó có thể chứa widget bắt nguồn từ StatefulWidget . Bản chất động của ứng dụng là thông qua hành vi tương tác của các vật dụng và trạng thái thay đổi trong quá trình tương tác. Ví dụ: chạm vào một nút bộ đếm sẽ tăng / giảm một trạng thái bên trong của bộ đếm và bản chất phản ứng của tiện ích Flutter sẽ tự động hiển thị lại tiện ích bằng cách sử dụng thông tin trạng thái mới.

Chúng ta sẽ tìm hiểu khái niệm về các widget StatefulWidget một cách chi tiết trong chương quản lý Nhà nước sắp tới .

Các widget cơ bản / độc lập với nền tảng

Flutter cung cấp một số lượng lớn các widget cơ bản để tạo giao diện người dùng đơn giản cũng như phức tạp theo cách độc lập với nền tảng. Chúng ta hãy xem một số widget cơ bản trong chương này.

Text

Text widget is used to display a piece of string. The style of the string can be set by using style property and TextStyle class. The sample code for this purpose is as follows −

Text('Hello World!', style: TextStyle(fontWeight: FontWeight.bold))

Text widget has a special constructor, Text.rich, which accepts the child of type TextSpan to specify the string with different style. TextSpan widget is recursive in nature and it accepts TextSpan as its children. The sample code for this purpose is as follows −

Text.rich( 
   TextSpan( 
      children: <TextSpan>[ 
         TextSpan(text: "Hello ", style:  
         TextStyle(fontStyle: FontStyle.italic)),  
         TextSpan(text: "World", style: 
         TextStyle(fontWeight: FontWeight.bold)),  
      ], 
   ), 
)

The most important properties of the Text widget are as follows −

  • maxLines, int − Maximum number of lines to show

  • overflow, TextOverFlow − Specify how visual overflow is handled using TextOverFlow class

  • style, TextStyle − Specify the style of the string using TextStyle class

  • textAlign, TextAlign − Alignment of the text like right, left, justify, etc., using TextAlign class

  • textDirection, TextDirection − Direction of text to flow, either left-to-right or right-to-left

Image

Image widget is used to display an image in the application. Image widget provides different constructors to load images from multiple sources and they are as follows −

  • Image − Generic image loader using ImageProvider

  • Image.asset − Load image from flutter project’s assets

  • Image.file − Load image from system folder

  • Image.memory − Load image from memory

  • Image.Network − Load image from network

The easiest option to load and display an image in Flutter is by including the image as assets of the application and load it into the widget on demand.

  • Create a folder, assets in the project folder and place the necessary images.

  • Specify the assets in the pubspec.yaml as shown below −

flutter: 
   assets: 
      - assets/smiley.png
  • Now, load and display the image in the application.

Image.asset('assets/smiley.png')
  • The complete source code of MyHomePage widget of the hello world application and the result is as shown below −.

class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold( 
         appBar: AppBar( title: Text(this.title), ), 
         body: Center( child: Image.asset("assets/smiley.png")),
      ); 
   }
}

The loaded image is as shown below −

The most important properties of the Image widget are as follows −

  • image, ImageProvider − Actual image to load

  • width, double − Width of the image

  • height, double − Height of the image

  • alignment, AlignmentGeometry − How to align the image within its bounds

Icon

Icon widget is used to display a glyph from a font described in IconData class. The code to load a simple email icon is as follows −

Icon(Icons.email)

The complete source code to apply it in hello world application is as follows −

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),),
         body: Center( child: Icon(Icons.email)),
      );
   }
}

The loaded icon is as shown below −

Since the core concept of Flutter is Everything is widget, Flutter incorporates a user interface layout functionality into the widgets itself. Flutter provides quite a lot of specially designed widgets like Container, Center, Align, etc., only for the purpose of laying out the user interface. Widgets build by composing other widgets normally use layout widgets. Let use learn the Flutter layout concept in this chapter.

Type of Layout Widgets

Layout widgets can be grouped into two distinct category based on its child −

  • Widget supporting a single child
  • Widget supporting multiple child

Let us learn both type of widgets and its functionality in the upcoming sections.

Single Child Widgets

In this category, widgets will have only one widget as its child and every widget will have a special layout functionality.

For example, Center widget just centers it child widget with respect to its parent widget and Container widget provides complete flexibility to place it child at any given place inside it using different option like padding, decoration, etc.,

Single child widgets are great options to create high quality widget having single functionality such as button, label, etc.,

The code to create a simple button using Container widget is as follows −

class MyButton extends StatelessWidget {
   MyButton({Key key}) : super(key: key); 

   @override 
   Widget build(BuildContext context) {
      return Container(
         decoration: const BoxDecoration(
            border: Border(
               top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
               right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
               bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
            ),
         ),
         child: Container(
            padding: const
            EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
            decoration: const BoxDecoration(
               border: Border(
                  top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
                  right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
                  bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
               ),
               color: Colors.grey,
            ),
            child: const Text(
               'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black)
            ), 
         ), 
      ); 
   }
}

Here, we have used two widgets – a Container widget and a Text widget. The result of the widget is as a custom button as shown below −

Let us check some of the most important single child layout widgets provided by Flutter

  • Padding − Used to arrange its child widget by the given padding. Here, padding can be provided by EdgeInsets class.

  • Align − Align its child widget within itself using the value of alignment property. The value for alignment property can be provided by FractionalOffset class. The FractionalOffset class specifies the offsets in terms of a distance from the top left.

Some of the possible values of offsets are as follows −

  • FractionalOffset(1.0, 0.0) represents the top right.

  • FractionalOffset(0.0, 1.0) represents the bottom left.

A sample code about offsets is shown below −

Center(
   child: Container(
      height: 100.0, 
      width: 100.0, 
      color: Colors.yellow, child: Align(
         alignment: FractionalOffset(0.2, 0.6),
         child: Container( height: 40.0, width:
            40.0, color: Colors.red,
         ), 
      ), 
   ), 
)
  • FittedBox − It scales the child widget and then positions it according to the specified fit.

  • AspectRatio − It attempts to size the child widget to the specified aspect ratio

  • ConstrainedBox

  • Baseline

  • FractinallySizedBox

  • IntrinsicHeight

  • IntrinsicWidth

  • LiimitedBox

  • OffStage

  • OverflowBox

  • SizedBox

  • SizedOverflowBox

  • Transform

  • CustomSingleChildLayout

Our hello world application is using material based layout widgets to design the home page. Let us modify our hello world application to build the home page using basic layout widgets as specified below −

  • Container − Generic, single child, box based container widget with alignment, padding, border and margin along with rich styling features.

  • Center − Simple, Single child container widget, which centers its child widget.

The modified code of the MyHomePage and MyApp widget is as below −

class MyApp extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return MyHomePage(title: "Hello World demo app");
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;
   @override
   Widget build(BuildContext context) {
      return Container(
         decoration: BoxDecoration(color: Colors.white,),
         padding: EdgeInsets.all(25), child: Center(
            child:Text(
               'Hello World', style: TextStyle(
                  color: Colors.black, letterSpacing: 0.5, fontSize: 20,
               ),
               textDirection: TextDirection.ltr,
            ),
         )
      );
   }
}

Here,

  • Container widget is the top level or root widget. Container is configured using decoration and padding property to layout its content.

  • BoxDecoration has many properties like color, border, etc., to decorate the Container widget and here, color is used to set the color of the container.

  • padding of the Container widget is set by using dgeInsets class, which provides the option to specify the padding value.

  • Center is the child widget of the Container widget. Again, Text is the child of the Center widget. Text is used to show message and Center is used to center the text message with respect to the parent widget, Container.

The final result of the code given above is a layout sample as shown below −

Multiple Child Widgets

In this category, a given widget will have more than one child widgets and the layout of each widget is unique.

For example, Row widget allows the laying out of its children in horizontal direction, whereas Column widget allows laying out of its children in vertical direction. By composing Row and Column, widget with any level of complexity can be built.

Let us learn some of the frequently used widgets in this section.

  • Row − Allows to arrange its children in a horizontal manner.

  • Column − Allows to arrange its children in a vertical manner.

  • ListView − Allows to arrange its children as list.

  • GridView − Allows to arrange its children as gallery.

  • Expanded − Used to make the children of Row and Column widget to occupy the maximum possible area.

  • Table − Table based widget.

  • Flow − Flow based widget.

  • Stack − Stack based widget.

Advanced Layout Application

In this section, let us learn how to create a complex user interface of product listing with custom design using both single and multiple child layout widgets.

For this purpose, follow the sequence given below −

  • Create a new Flutter application in Android studio, product_layout_app.

  • Replace the main.dart code with folowing code −

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget {
   // This widget is the root of your application.
   @override 
   Widget build(BuildContext context) {
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData( 
         primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page'),
      ); 
   } 
} 
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
      
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),), 
         body: Center(child: Text( 'Hello World', )), 
      ); 
   }
}
  • Here,

  • We have created MyHomePage widget by extending StatelessWidget instead of default StatefulWidget and then removed the relevant code.

  • Now, create a new widget, ProductBox according to the specified design as shown below −

  • The code for the ProductBox is as follows.

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) 
      : super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 

   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), height: 120,  child: Card( 
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
                  Image.asset("assets/appimages/" +image), Expanded(
                     child: Container(
                        padding: EdgeInsets.all(5), child: Column(
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[ 
                              
                              Text(this.name, style: TextStyle(fontWeight: 
                                 FontWeight.bold)), Text(this.description), 
                              Text("Price: " + this.price.toString()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}
  • Please observe the following in the code −

  • ProductBox has used four arguments as specified below −

    • name - Product name

    • description - Product description

    • price - Price of the product

    • image - Image of the product

  • ProductBox uses seven build-in widgets as specified below −

    • Container
    • Expanded
    • Row
    • Column
    • Card
    • Text
    • Image
  • ProductBox is designed using the above mentioned widget. The arrangement or hierarchy of the widget is specified in the diagram shown below −

  • Now, place some dummy image (see below) for product information in the assets folder of the application and configure the assets folder in the pubspec.yaml file as shown below −

assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png

iPhone.png

Pixel.png

Laptop.png

Tablet.png

Pendrive.png

Floppy.png

Finally, Use the ProductBox widget in the MyHomePage widget as specified below −

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 

   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title:Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget> [
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
  • Ở đây, chúng tôi đã sử dụng ProductBox làm con của tiện ích ListView .

  • Mã hoàn chỉnh (main.dart) của ứng dụng bố cục sản phẩm (product_layout_app) như sau:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', theme: ThemeData(
            primarySwatch: Colors.blue,
         ), 
         home: MyHomePage(title: 'Product layout demo home page'), 
      );
   }
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   @override 
   Widget build(BuildContext context) { 
      return Scaffold( 
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[ 
               ProductBox(
                  name: "iPhone", 
                  description: "iPhone is the stylist phone ever", 
                  price: 1000, 
                  image: "iphone.png"
               ), 
               ProductBox( 
                  name: "Pixel",    
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox( 
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox( 
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ],
         )
      );
   }
}
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key); 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 120, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column(    
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ),
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      );
   }
}

Kết quả cuối cùng của ứng dụng như sau:

Cử chỉ chủ yếu là cách để người dùng tương tác với ứng dụng di động (hoặc bất kỳ thiết bị dựa trên cảm ứng nào). Cử chỉ thường được định nghĩa là bất kỳ hành động / chuyển động vật lý nào của người dùng nhằm mục đích kích hoạt một điều khiển cụ thể của thiết bị di động. Các cử chỉ đơn giản như chạm vào màn hình của thiết bị di động cho đến các hành động phức tạp hơn được sử dụng trong các ứng dụng trò chơi.

Một số cử chỉ được sử dụng rộng rãi được đề cập ở đây -

  • Tap - Chạm vào bề mặt của thiết bị bằng đầu ngón tay trong khoảng thời gian ngắn rồi thả ngón tay ra.

  • Double Tap - Khai thác hai lần trong một thời gian ngắn.

  • Drag - Chạm vào bề mặt của thiết bị bằng đầu ngón tay, sau đó di chuyển đầu ngón tay một cách ổn định và cuối cùng thả ngón tay ra.

  • Flick - Tương tự như kéo, nhưng làm theo cách nhanh hơn.

  • Pinch - Dùng hai ngón tay véo bề mặt của thiết bị.

  • Spread/Zoom - Đối lập với véo von.

  • Panning - Chạm vào bề mặt của thiết bị bằng đầu ngón tay và di chuyển theo bất kỳ hướng nào mà không cần thả ngón tay ra.

Flutter cung cấp hỗ trợ tuyệt vời cho tất cả các loại cử chỉ thông qua tiện ích độc quyền của nó, GestureDetector. GestureDetector là một tiện ích không trực quan chủ yếu được sử dụng để phát hiện cử chỉ của người dùng. Để xác định một cử chỉ được nhắm mục tiêu trên một tiện ích, tiện ích có thể được đặt bên trong tiện ích GestureDetector. GestureDetector sẽ nắm bắt cử chỉ và gửi nhiều sự kiện dựa trên cử chỉ.

Dưới đây là một số cử chỉ và sự kiện tương ứng:

  • Tap
    • onTapDown
    • onTapUp
    • onTap
    • onTapCancel
  • Nhấn đúp
    • onDoubleTap
  • Nhấn và giữ
    • onLongPress
  • Kéo dọc
    • onVerticalDragStart
    • onVerticalDragUpdate
    • onVerticalDragEnd
  • Kéo ngang
    • onHorizontalDragStart
    • onHorizontalDragUpdate
    • onHorizontalDragEnd
  • Pan
    • onPanStart
    • onPanUpdate
    • onPanEnd

Bây giờ, chúng ta hãy sửa đổi ứng dụng hello world để bao gồm tính năng phát hiện cử chỉ và cố gắng hiểu khái niệm.

  • Thay đổi nội dung nội dung của tiện ích MyHomePage như hình dưới đây -

body: Center( 
   child: GestureDetector( 
      onTap: () { 
         _showDialog(context); 
      }, 
      child: Text( 'Hello World', ) 
   ) 
),
  • Quan sát rằng ở đây chúng ta đã đặt tiện ích GestureDetector phía trên tiện ích Văn bản trong phân cấp tiện ích, nắm bắt sự kiện onTap và cuối cùng hiển thị một cửa sổ hộp thoại.

  • Triển khai hàm * _showDialog * để trình bày hộp thoại khi người dùng nhấn vào thông báo hello world . Nó sử dụng generic ShowDialogAlertDialog widget để tạo một widget thoại mới. Mã được hiển thị bên dưới -

// user defined function void _showDialog(BuildContext context) { 
   // flutter defined function 
   showDialog( 
      context: context, builder: (BuildContext context) { 
         // return object of type Dialog
         return AlertDialog( 
            title: new Text("Message"), 
            content: new Text("Hello World"),   
            actions: <Widget>[ 
               new FlatButton( 
                  child: new Text("Close"),  
                  onPressed: () {   
                     Navigator.of(context).pop();  
                  }, 
               ), 
            ], 
         ); 
      }, 
   ); 
}
  • Ứng dụng sẽ tải lại trong thiết bị bằng tính năng Tải lại nóng. Bây giờ, chỉ cần nhấp vào thông báo, Hello World và nó sẽ hiển thị hộp thoại như bên dưới:

  • Bây giờ, đóng hộp thoại bằng cách nhấp vào tùy chọn đóng trong hộp thoại.

  • Mã hoàn chỉnh (main.dart) như sau:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application.    
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Hello World Demo Application', 
         theme: ThemeData( primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Home page'), 
      ); 
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   // user defined function 
   void _showDialog(BuildContext context) { 
      // flutter defined function showDialog( 
         context: context, builder: (BuildContext context) { 
            // return object of type Dialog return AlertDialog(
               title: new Text("Message"), 
               content: new Text("Hello World"),   
               actions: <Widget>[
                  new FlatButton(
                     child: new Text("Close"), 
                     onPressed: () {   
                        Navigator.of(context).pop();  
                     }, 
                  ), 
               ],
            );
         },
      );
   }
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text(this.title),),
         body: Center(
            child: GestureDetector( 
               onTap: () {
                  _showDialog(context);
               },
            child: Text( 'Hello World', )
            )
         ),
      );
   }
}

Cuối cùng, Flutter cũng cung cấp một cơ chế phát hiện cử chỉ cấp thấp thông qua tiện ích Listener . Nó sẽ phát hiện tất cả các tương tác của người dùng và sau đó gửi đi các sự kiện sau:

  • PointerDownEvent
  • PointerMoveEvent
  • PointerUpEvent
  • PointerCancelEvent

Flutter cũng cung cấp một tập hợp nhỏ các widget để thực hiện các cử chỉ cụ thể cũng như nâng cao. Các tiện ích được liệt kê bên dưới -

  • Dismissible - Hỗ trợ cử chỉ vuốt để loại bỏ tiện ích.

  • Draggable - Hỗ trợ cử chỉ kéo để di chuyển widget.

  • LongPressDraggable - Hỗ trợ cử chỉ kéo để di chuyển tiện ích con, khi tiện ích con của nó cũng có thể kéo được.

  • DragTarget- Chấp nhận mọi tiện ích có thể kéo được

  • IgnorePointer - Ẩn widget và con của nó khỏi quá trình phát hiện cử chỉ.

  • AbsorbPointer - Tự dừng quá trình phát hiện cử chỉ và do đó bất kỳ tiện ích con chồng chéo nào cũng không thể tham gia vào quá trình phát hiện cử chỉ và do đó, không có sự kiện nào được đưa ra.

  • Scrollable - Hỗ trợ cuộn nội dung có sẵn bên trong widget.

Quản lý trạng thái trong một ứng dụng là một trong những quá trình quan trọng và cần thiết trong vòng đời của một ứng dụng.

Hãy để chúng tôi xem xét một ứng dụng giỏ hàng đơn giản.

  • Người dùng sẽ đăng nhập bằng thông tin đăng nhập của họ vào ứng dụng.

  • Khi người dùng đã đăng nhập, ứng dụng sẽ duy trì thông tin chi tiết người dùng đã đăng nhập trên tất cả màn hình.

  • Một lần nữa, khi người dùng chọn một sản phẩm và lưu vào giỏ hàng, thông tin giỏ hàng sẽ tồn tại giữa các trang cho đến khi người dùng kiểm tra giỏ hàng.

  • Người dùng và thông tin giỏ hàng của họ tại bất kỳ trường hợp nào được gọi là trạng thái của ứng dụng tại trường hợp đó.

Quản lý nhà nước có thể được chia thành hai loại dựa trên thời gian trạng thái cụ thể tồn tại trong một ứng dụng.

  • Ephemeral- Kéo dài trong vài giây như trạng thái hiện tại của hoạt ảnh hoặc một trang đơn lẻ như xếp hạng hiện tại của sản phẩm. Flutter hỗ trợ nó thông qua StatefulWidget.

  • app state- Cuối cùng cho toàn bộ ứng dụng như chi tiết người dùng đã đăng nhập, thông tin giỏ hàng, v.v., Flutter hỗ trợ thông qua phạm vi mô hình.

Điều hướng và định tuyến

Trong bất kỳ ứng dụng nào, việc điều hướng từ trang / màn hình này sang trang / màn hình khác xác định quy trình làm việc của ứng dụng. Cách xử lý điều hướng của một ứng dụng được gọi là Định tuyến. Flutter cung cấp một lớp định tuyến cơ bản - MaterialPageRoute và hai phương thức - Navigator.push và Navigator.pop, để xác định luồng công việc của một ứng dụng.

MaterialPageRoute

MaterialPageRoute là một tiện ích được sử dụng để hiển thị giao diện người dùng của nó bằng cách thay thế toàn bộ màn hình bằng hoạt ảnh dành riêng cho nền tảng.

MaterialPageRoute(builder: (context) => Widget())

Ở đây, trình xây dựng sẽ chấp nhận một hàm để xây dựng nội dung của nó bằng cách bổ sung ngữ cảnh hiện tại của ứng dụng.

Navigation.push

Navigation.push được sử dụng để điều hướng đến màn hình mới bằng tiện ích MaterialPageRoute.

Navigator.push( context, MaterialPageRoute(builder: (context) => Widget()), );

Navigation.pop

Navigation.pop được sử dụng để điều hướng đến màn hình trước đó.

Navigator.pop(context);

Hãy để chúng tôi tạo một ứng dụng mới để hiểu rõ hơn về khái niệm điều hướng.

Tạo ứng dụng Flutter mới trong Android studio, product_nav_app

  • Sao chép thư mục nội dung từ product_nav_app sang product_state_app và thêm nội dung bên trong tệp pubspec.yaml.

flutter:
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • Thay thế mã khởi động mặc định (main.dart) bằng mã khởi động của chúng tôi.

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) { 
      return MaterialApp( 
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage(
            title: 'Product state demo home page'
         ),
      );
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key);
   final String title;
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(
            title: Text(this.title), 
         ), 
         body: Center(
            child: Text('Hello World',)
         ), 
      ); 
   } 
}
  • Hãy để chúng tôi tạo lớp Sản phẩm để sắp xếp thông tin sản phẩm.

class Product { 
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   Product(this.name, this.description, this.price, this.image); 
}
  • Hãy để chúng tôi viết một phương thức getProducts trong lớp Sản phẩm để tạo các bản ghi sản phẩm giả của chúng tôi.

static List<Product> getProducts() {
   List<Product> items = <Product>[]; 
   
   items.add(
      Product( 
         "Pixel", 
         "Pixel is the most feature-full phone ever", 800, 
         "pixel.png"
      )
   ); 
   items.add(
      Product(
         "Laptop", 
         "Laptop is most productive development tool", 
         2000, "
         laptop.png"
      )
   ); 
   items.add(
      Product( 
         "Tablet", 
         "Tablet is the most useful device ever for meeting", 
         1500, 
         "tablet.png"
      )
   ); 
   items.add(
      Product( 
         "Pendrive", 
         "Pendrive is useful storage medium",
         100, 
         "pendrive.png"
      )
   ); 
   items.add(
      Product( 
         "Floppy Drive", 
         "Floppy drive is useful rescue storage medium", 
         20, 
         "floppy.png"
      )
   ); 
   return items; 
}
import product.dart in main.dart
import 'Product.dart';
  • Hãy để chúng tôi đưa vào widget mới của chúng tôi, RatingBox.

class RatingBox extends StatefulWidget {
   @override 
   _RatingBoxState createState() =>_RatingBoxState(); 
} 
class _RatingBoxState extends State<RatingBox> {
   int _rating = 0; 
   void _setRatingAsOne() {
      setState(() {
         _rating = 1; 
      }); 
   } 
   void _setRatingAsTwo() {
      setState(() {
         _rating = 2; 
      }); 
   }
   void _setRatingAsThree() {
      setState(() {
         _rating = 3;
      });
   }
   Widget build(BuildContext context) {
      double _size = 20; 
      print(_rating); 
      return Row(
         mainAxisAlignment: MainAxisAlignment.end, 
         crossAxisAlignment: CrossAxisAlignment.end, 
         mainAxisSize: MainAxisSize.max, 
         children: <Widget>[
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 1? 
                     Icon( 
                        Icons.star, 
                        size: _size, 
                     ) 
                     : Icon(
                        Icons.star_border, 
                        size: _size, 
                     )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsOne, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 2? 
                     Icon(
                        Icons.star, 
                        size: _size, 
                     ) 
                     : Icon(
                        Icons.star_border, 
                        size: _size, 
                     )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsTwo, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 3 ? 
                     Icon(
                        Icons.star, 
                        size: _size, 
                     ) 
                     : Icon( 
                        Icons.star_border, 
                        size: _size, 
                     )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsThree, 
                  iconSize: _size, 
               ), 
            ), 
         ], 
      ); 
   }
}
  • Hãy để chúng tôi sửa đổi tiện ích ProductBox để hoạt động với lớp Sản phẩm mới của chúng tôi.

class ProductBox extends StatelessWidget {    
   ProductBox({Key key, this.item}) : super(key: key); 
   final Product item; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card( 
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + this.item.image), 
                  Expanded(
                     child: Container(
                        padding: EdgeInsets.all(5), 
                        child: Column(
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[
                              Text(this.item.name, 
                              style: TextStyle(fontWeight: FontWeight.bold)), 
                              Text(this.item.description), 
                              Text("Price: " + this.item.price.toString()), 
                              RatingBox(), 
                           ], 
                        )
                     )
                  )
               ]
            ), 
         )
      ); 
   }
}

Hãy để chúng tôi viết lại tiện ích MyHomePage của chúng tôi để làm việc với Mô hình sản phẩm và liệt kê tất cả các sản phẩm bằng cách sử dụng ListView.

class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   final items = Product.getProducts(); 
   
   @override 
   Widget build(BuildContext context) { 
      return Scaffold( appBar: AppBar(title: Text("Product Navigation")), 
      body: ListView.builder( 
         itemCount: items.length, 
         itemBuilder: (context, index) {
            return GestureDetector( 
               child: ProductBox(item: items[index]), 
               onTap: () { 
                  Navigator.push( 
                     context, MaterialPageRoute( 
                        builder: (context) => ProductPage(item: items[index]), 
                     ), 
                  ); 
               }, 
            ); 
         }, 
      )); 
   } 
}

Ở đây, chúng tôi đã sử dụng MaterialPageRoute để điều hướng đến trang chi tiết sản phẩm.

  • Bây giờ, chúng ta hãy thêm ProductPage để hiển thị chi tiết sản phẩm.

class ProductPage extends StatelessWidget { 
   ProductPage({Key key, this.item}) : super(key: key); 
   final Product item; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar( 
            title: Text(this.item.name), 
         ), 
         body: Center(
            child: Container(
               padding: EdgeInsets.all(0), 
               child: Column(
                  mainAxisAlignment: MainAxisAlignment.start, 
                  crossAxisAlignment: CrossAxisAlignment.start, 
                  children: <Widget>[
                     Image.asset("assets/appimages/" + this.item.image), 
                     Expanded(
                        child: Container(
                           padding: EdgeInsets.all(5), 
                           child: Column(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[
                                 Text(
                                    this.item.name, style: TextStyle(
                                       fontWeight: FontWeight.bold
                                    )
                                 ), 
                                 Text(this.item.description), 
                                 Text("Price: " + this.item.price.toString()), 
                                 RatingBox(),
                              ], 
                           )
                        )
                     )
                  ]
               ), 
            ), 
         ), 
      ); 
   } 
}

Mã hoàn chỉnh của ứng dụng như sau:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class Product {
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   Product(this.name, this.description, this.price, this.image); 
   
   static List<Product> getProducts() {
      List<Product> items = <Product>[]; 
      items.add(
         Product(
            "Pixel", 
            "Pixel is the most featureful phone ever", 
            800, 
            "pixel.png"
         )
      );
      items.add(
         Product(
            "Laptop", 
            "Laptop is most productive development tool", 
            2000, 
            "laptop.png"
         )
      ); 
      items.add(
         Product(
            "Tablet", 
            "Tablet is the most useful device ever for meeting", 
            1500, 
            "tablet.png"
         )
      ); 
      items.add(
         Product( 
            "Pendrive", 
            "iPhone is the stylist phone ever", 
            100, 
            "pendrive.png"
         )
      ); 
      items.add(
         Product(
            "Floppy Drive", 
            "iPhone is the stylist phone ever", 
            20, 
            "floppy.png"
         )
      ); 
      items.add(
         Product(
            "iPhone", 
            "iPhone is the stylist phone ever", 
            1000, 
            "iphone.png"
         )
      ); 
      return items; 
   }
}
class MyApp extends StatelessWidget {
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage(title: 'Product Navigation demo home page'), 
      ); 
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   final items = Product.getProducts(); 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Navigation")), 
         body: ListView.builder( 
            itemCount: items.length, 
            itemBuilder: (context, index) { 
               return GestureDetector( 
                  child: ProductBox(item: items[index]), 
                  onTap: () { 
                     Navigator.push( 
                        context, 
                        MaterialPageRoute( 
                           builder: (context) => ProductPage(item: items[index]), 
                        ), 
                     ); 
                  }, 
               ); 
            }, 
         )
      ); 
   }
} 
class ProductPage extends StatelessWidget {
   ProductPage({Key key, this.item}) : super(key: key); 
   final Product item; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(
            title: Text(this.item.name), 
         ), 
         body: Center(
            child: Container( 
               padding: EdgeInsets.all(0), 
               child: Column( 
                  mainAxisAlignment: MainAxisAlignment.start, 
                  crossAxisAlignment: CrossAxisAlignment.start, 
                  children: <Widget>[ 
                     Image.asset("assets/appimages/" + this.item.image), 
                     Expanded( 
                        child: Container( 
                           padding: EdgeInsets.all(5), 
                           child: Column( 
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                              children: <Widget>[ 
                                 Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)), 
                                 Text(this.item.description), 
                                 Text("Price: " + this.item.price.toString()), 
                                 RatingBox(), 
                              ], 
                           )
                        )
                     ) 
                  ]
               ), 
            ), 
         ), 
      ); 
   } 
}
class RatingBox extends StatefulWidget { 
   @override 
   _RatingBoxState createState() => _RatingBoxState(); 
} 
class _RatingBoxState extends State<RatingBox> { 
   int _rating = 0;
   void _setRatingAsOne() {
      setState(() {
         _rating = 1; 
      }); 
   }
   void _setRatingAsTwo() {
      setState(() {
         _rating = 2; 
      }); 
   } 
   void _setRatingAsThree() { 
      setState(() {
         _rating = 3; 
      }); 
   }
   Widget build(BuildContext context) {
      double _size = 20; 
      print(_rating); 
      return Row(
         mainAxisAlignment: MainAxisAlignment.end, 
         crossAxisAlignment: CrossAxisAlignment.end, 
         mainAxisSize: MainAxisSize.max, 
         children: <Widget>[
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 1 ? Icon( 
                        Icons.star, 
                        size: _size, 
                     ) 
                     : Icon( 
                        Icons.star_border, 
                        size: _size, 
                     )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsOne, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton( 
                  icon: (
                     _rating >= 2 ? 
                     Icon( 
                        Icons.star, 
                        size: _size, 
                     ) 
                     : Icon( 
                        Icons.star_border, 
                        size: _size, 
                     )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsTwo, 
                  iconSize: _size, 
               ), 
            ), 
            Container(
               padding: EdgeInsets.all(0), 
               child: IconButton(
                  icon: (
                     _rating >= 3 ? 
                     Icon( 
                        Icons.star, 
                        size: _size, 
                     )
                     : Icon( 
                        Icons.star_border, 
                        size: _size, 
                     )
                  ), 
                  color: Colors.red[500], 
                  onPressed: _setRatingAsThree, 
                  iconSize: _size, 
               ), 
            ), 
         ], 
      ); 
   } 
} 
class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.item}) : super(key: key); 
   final Product item; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + this.item.image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)), Text(this.item.description), 
                              Text("Price: " + this.item.price.toString()), 
                              RatingBox(), 
                           ], 
                        )
                     )
                  ) 
               ]
            ), 
         )
      ); 
   } 
}

Chạy ứng dụng và nhấp vào bất kỳ một trong các mục sản phẩm. Nó sẽ hiển thị trang chi tiết có liên quan. Chúng ta có thể chuyển đến trang chủ bằng cách nhấp vào nút quay lại. Trang danh sách sản phẩm và trang chi tiết sản phẩm của ứng dụng được hiển thị như sau:

Hoạt ảnh là một thủ tục phức tạp trong bất kỳ ứng dụng di động nào. Bất chấp sự phức tạp của nó, Animation nâng cao trải nghiệm người dùng lên một tầm cao mới và mang lại sự tương tác phong phú cho người dùng. Do sự phong phú của nó, hoạt ảnh trở thành một phần không thể thiếu trong ứng dụng di động hiện đại. Khung công tác Flutter nhận ra tầm quan trọng của Hoạt ảnh và cung cấp một khuôn khổ đơn giản và trực quan để phát triển tất cả các loại hoạt ảnh.

Giới thiệu

Hoạt ảnh là một quá trình hiển thị một loạt hình ảnh / bức tranh theo một thứ tự cụ thể trong một khoảng thời gian cụ thể để tạo ra ảo giác về chuyển động. Các khía cạnh quan trọng nhất của hoạt ảnh như sau:

  • Hoạt ảnh có hai giá trị riêng biệt: Giá trị bắt đầu và Giá trị kết thúc. Hoạt ảnh bắt đầu từ giá trị Bắt đầu và trải qua một loạt các giá trị trung gian và cuối cùng kết thúc ở giá trị Kết thúc. Ví dụ: để tạo hoạt ảnh cho một tiện ích con mờ đi, giá trị ban đầu sẽ là độ mờ hoàn toàn và giá trị cuối cùng sẽ là độ mờ bằng không.

  • Các giá trị trung gian có thể là tuyến tính hoặc phi tuyến tính (đường cong) về bản chất và nó có thể được cấu hình. Hiểu rằng hoạt ảnh hoạt động khi nó được định cấu hình. Mỗi cấu hình cung cấp một cảm giác khác nhau cho hoạt ảnh. Ví dụ: làm mờ một widget sẽ có tính chất tuyến tính trong khi độ nảy của một quả bóng sẽ không tuyến tính về bản chất.

  • Thời lượng của quá trình hoạt ảnh ảnh hưởng đến tốc độ (độ chậm hoặc nhanh) của hoạt ảnh.

  • Khả năng kiểm soát quá trình hoạt ảnh như bắt đầu hoạt ảnh, dừng hoạt ảnh, lặp lại hoạt ảnh để đặt số lần, đảo ngược quá trình hoạt ảnh, v.v.

  • Trong Flutter, hệ thống hoạt ảnh không thực hiện bất kỳ hoạt ảnh thực nào. Thay vào đó, nó chỉ cung cấp các giá trị cần thiết ở mọi khung hình để hiển thị hình ảnh.

Lớp dựa trên hoạt ảnh

Hệ thống hoạt hình Flutter dựa trên các đối tượng Animation. Các lớp hoạt ảnh cốt lõi và cách sử dụng nó như sau:

Hoạt hình

Tạo giá trị nội suy giữa hai số trong một khoảng thời gian nhất định. Các lớp Hoạt hình phổ biến nhất là -

  • Animation<double> - nội suy các giá trị giữa hai số thập phân

  • Animation<Color> - nội suy màu sắc giữa hai màu

  • Animation<Size> - nội suy kích thước giữa hai kích thước

  • AnimationController- Đối tượng Animation đặc biệt để điều khiển hoạt ảnh của chính nó. Nó tạo ra các giá trị mới bất cứ khi nào ứng dụng sẵn sàng cho một khung mới. Nó hỗ trợ hoạt ảnh dựa trên tuyến tính và giá trị bắt đầu từ 0,0 đến 1,0

controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);

Ở đây, bộ điều khiển kiểm soát hoạt ảnh và tùy chọn thời lượng kiểm soát thời lượng của quá trình hoạt ảnh. vsync là một tùy chọn đặc biệt được sử dụng để tối ưu hóa tài nguyên được sử dụng trong hoạt ảnh.

CurvedAnimation

Tương tự như AnimationController nhưng hỗ trợ hoạt ảnh phi tuyến tính. CurvedAnimation có thể được sử dụng cùng với đối tượng Animation như bên dưới:

controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); 
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)

Tween <T>

Bắt nguồn từ Animatable <T> và được sử dụng để tạo các số giữa hai số bất kỳ khác 0 và 1. Nó có thể được sử dụng cùng với đối tượng Animation bằng cách sử dụng phương thức animate và chuyển đối tượng Animation thực tế.

AnimationController controller = AnimationController( 
   duration: const Duration(milliseconds: 1000), 
vsync: this); Animation<int> customTween = IntTween(
   begin: 0, end: 255).animate(controller);
  • Tween cũng có thể được sử dụng cùng với CurvedAnimation như bên dưới:

AnimationController controller = AnimationController(
   duration: const Duration(milliseconds: 500), vsync: this); 
final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut); 
Animation<int> customTween = IntTween(begin: 0, end: 255).animate(curve);

Ở đây, controller là bộ điều khiển hoạt ảnh thực tế. Đường cong cung cấp loại không tuyến tính và customTween cung cấp phạm vi tùy chỉnh từ 0 đến 255.

Luồng công việc của Flutter Animation

Quy trình làm việc của hoạt ảnh như sau:

  • Xác định và khởi động bộ điều khiển hoạt ảnh trong initState của StatefulWidget.

AnimationController(duration: const Duration(seconds: 2), vsync: this); 
animation = Tween<double>(begin: 0, end: 300).animate(controller); 
controller.forward();
  • Thêm trình nghe dựa trên hoạt ảnh, addListener để thay đổi trạng thái của tiện ích.

animation = Tween<double>(begin: 0, end: 300).animate(controller) ..addListener(() {
   setState(() { 
      // The state that has changed here is the animation object’s value. 
   }); 
});
  • Có thể sử dụng các widget tích hợp, AnimatedWidget và AnimatedBuilder để bỏ qua quá trình này. Cả hai widget đều chấp nhận đối tượng Animation và nhận các giá trị hiện tại cần thiết cho hoạt ảnh.

  • Nhận các giá trị hoạt ảnh trong quá trình xây dựng tiện ích con và sau đó áp dụng nó cho chiều rộng, chiều cao hoặc bất kỳ thuộc tính nào có liên quan thay vì giá trị ban đầu.

child: Container( 
   height: animation.value, 
   width: animation.value, 
   child: <Widget>, 
)

Ứng dụng làm việc

Hãy để chúng tôi viết một ứng dụng dựa trên hoạt ảnh đơn giản để hiểu khái niệm về hoạt ảnh trong khung Flutter.

  • Tạo ứng dụng Flutter mới trong Android studio, product_animation_app.

  • Sao chép thư mục nội dung từ product_nav_app sang product_animation_app và thêm nội dung bên trong tệp pubspec.yaml.

flutter: 
   assets: 
   - assets/appimages/floppy.png 
   - assets/appimages/iphone.png 
   - assets/appimages/laptop.png 
   - assets/appimages/pendrive.png 
   - assets/appimages/pixel.png 
   - assets/appimages/tablet.png
  • Loại bỏ mã khởi động mặc định (main.dart).

  • Thêm nhập và chức năng chính cơ bản.

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp());
  • Tạo tiện ích MyApp có nguồn gốc từ StatefulWidgtet.

class MyApp extends StatefulWidget { 
   _MyAppState createState() => _MyAppState(); 
}
  • Tạo tiện ích _MyAppState và triển khai initState và loại bỏ ngoài phương pháp xây dựng mặc định.

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin { 
   Animation<double> animation; 
   AnimationController controller; 
   @override void initState() {
      super.initState(); 
      controller = AnimationController(
         duration: const Duration(seconds: 10), vsync: this
      ); 
      animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller); 
      controller.forward(); 
   } 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      controller.forward(); 
      return MaterialApp(
         title: 'Flutter Demo',
         theme: ThemeData(primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page', animation: animation,)
      ); 
   } 
   @override 
   void dispose() {
      controller.dispose();
      super.dispose();
   }
}

Đây,

  • Trong phương thức initState, chúng ta đã tạo một đối tượng điều khiển hoạt ảnh (controller), một đối tượng hoạt ảnh (animation) và bắt đầu hoạt ảnh bằng controller.earch.

  • Trong phương thức giải quyết, chúng ta đã xử lý đối tượng điều khiển hoạt ảnh (controller).

  • Trong phương thức xây dựng, gửi hoạt ảnh tới tiện ích MyHomePage thông qua hàm tạo. Giờ đây, tiện ích MyHomePage có thể sử dụng đối tượng hoạt ảnh để tạo hoạt ảnh cho nội dung của nó.

  • Bây giờ, hãy thêm tiện ích ProductBox

class ProductBox extends StatelessWidget {
   ProductBox({Key key, this.name, this.description, this.price, this.image})
      : super(key: key);
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card( 
            child: Row( 
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded( 
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(this.name, style: 
                                 TextStyle(fontWeight: FontWeight.bold)), 
                              Text(this.description), 
                                 Text("Price: " + this.price.toString()), 
                           ], 
                        )
                     )
                  )
               ]
            )
         )
      ); 
   }
}
  • Tạo một widget mới, MyAnimatedWidget để tạo hoạt ảnh mờ dần đơn giản bằng cách sử dụng độ mờ.

class MyAnimatedWidget extends StatelessWidget { 
   MyAnimatedWidget({this.child, this.animation}); 
      
   final Widget child; 
   final Animation<double> animation; 
   
   Widget build(BuildContext context) => Center( 
   child: AnimatedBuilder(
      animation: animation, 
      builder: (context, child) => Container( 
         child: Opacity(opacity: animation.value, child: child), 
      ), 
      child: child), 
   ); 
}
  • Ở đây, chúng tôi đã sử dụng AniatedBuilder để làm hoạt ảnh của chúng tôi. AnimatedBuilder là một widget xây dựng nội dung của nó trong khi thực hiện hoạt ảnh cùng một lúc. Nó chấp nhận một đối tượng hoạt ảnh để nhận giá trị hoạt ảnh hiện tại. Chúng tôi đã sử dụng giá trị hoạt ảnh, animation.value để đặt độ mờ của tiện ích con. Trên thực tế, tiện ích con sẽ tạo hoạt ảnh cho tiện ích con bằng cách sử dụng khái niệm độ mờ.

  • Cuối cùng, tạo tiện ích MyHomePage và sử dụng đối tượng hoạt ảnh để tạo hoạt ảnh cho bất kỳ nội dung nào của nó.

class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title, this.animation}) : super(key: key); 
   
   final String title; 
   final Animation<double> 
   animation; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")),body: ListView(
            shrinkWrap: true,
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               FadeTransition(
                  child: ProductBox(
                     name: "iPhone", 
                     description: "iPhone is the stylist phone ever", 
                     price: 1000, 
                     image: "iphone.png"
                  ), opacity: animation
               ), 
               MyAnimatedWidget(child: ProductBox(
                  name: "Pixel", 
                  description: "Pixel is the most featureful phone ever", 
                  price: 800, 
                  image: "pixel.png"
               ), animation: animation), 
               ProductBox(
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox(
                  name: "Tablet", 
                  description: "Tablet is the most useful device ever for meeting", 
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ),
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ),
            ],
         )
      );
   }
}

Ở đây, chúng tôi đã sử dụng FadeAnimation và MyAnimationWidget để tạo hoạt ảnh cho hai mục đầu tiên trong danh sách. FadeAnimation là một lớp hoạt ảnh tích hợp, chúng tôi đã sử dụng để tạo hoạt ảnh con của nó bằng cách sử dụng khái niệm độ mờ.

  • Mã hoàn chỉnh như sau:

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 

class MyApp extends StatefulWidget { 
   _MyAppState createState() => _MyAppState(); 
} 
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
   Animation<double> animation; 
   AnimationController controller; 
   
   @override 
   void initState() {
      super.initState(); 
      controller = AnimationController(
         duration: const Duration(seconds: 10), vsync: this); 
      animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller); 
      controller.forward(); 
   } 
   // This widget is the root of your application. 
   @override 
   Widget build(BuildContext context) {
      controller.forward(); 
      return MaterialApp( 
         title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue,), 
         home: MyHomePage(title: 'Product layout demo home page', animation: animation,) 
      ); 
   } 
   @override 
   void dispose() {
      controller.dispose();
      super.dispose(); 
   } 
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title, this.animation}): super(key: key);
   final String title; 
   final Animation<double> animation; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(title: Text("Product Listing")), 
         body: ListView(
            shrinkWrap: true, 
            padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), 
            children: <Widget>[
               FadeTransition(
                  child: ProductBox(
                     name: "iPhone", 
                     description: "iPhone is the stylist phone ever", 
                     price: 1000, 
                     image: "iphone.png"
                  ), 
                  opacity: animation
               ), 
               MyAnimatedWidget(
                  child: ProductBox( 
                     name: "Pixel", 
                     description: "Pixel is the most featureful phone ever", 
                     price: 800, 
                     image: "pixel.png"
                  ), 
                  animation: animation
               ), 
               ProductBox( 
                  name: "Laptop", 
                  description: "Laptop is most productive development tool", 
                  price: 2000, 
                  image: "laptop.png"
               ), 
               ProductBox(
                  name: "Tablet",
                  description: "Tablet is the most useful device ever for meeting",
                  price: 1500, 
                  image: "tablet.png"
               ), 
               ProductBox(
                  name: "Pendrive", 
                  description: "Pendrive is useful storage medium", 
                  price: 100, 
                  image: "pendrive.png"
               ), 
               ProductBox(
                  name: "Floppy Drive", 
                  description: "Floppy drive is useful rescue storage medium", 
                  price: 20, 
                  image: "floppy.png"
               ), 
            ], 
         )
      ); 
   } 
} 
class ProductBox extends StatelessWidget { 
   ProductBox({Key key, this.name, this.description, this.price, this.image}) :
      super(key: key);
   final String name; 
   final String description; 
   final int price; 
   final String image; 
   Widget build(BuildContext context) {
      return Container(
         padding: EdgeInsets.all(2), 
         height: 140, 
         child: Card(
            child: Row(
               mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
               children: <Widget>[ 
                  Image.asset("assets/appimages/" + image), 
                  Expanded(
                     child: Container( 
                        padding: EdgeInsets.all(5), 
                        child: Column( 
                           mainAxisAlignment: MainAxisAlignment.spaceEvenly, 
                           children: <Widget>[ 
                              Text(
                                 this.name, style: TextStyle(
                                    fontWeight: FontWeight.bold
                                 )
                              ), 
                              Text(this.description), Text(
                                 "Price: " + this.price.toString()
                              ), 
                           ], 
                        )
                     )
                  ) 
               ]
            )
         )
      ); 
   } 
}
class MyAnimatedWidget extends StatelessWidget { 
   MyAnimatedWidget({this.child, this.animation}); 
   final Widget child; 
   final Animation<double> animation; 
 
   Widget build(BuildContext context) => Center( 
      child: AnimatedBuilder(
         animation: animation, 
         builder: (context, child) => Container( 
            child: Opacity(opacity: animation.value, child: child), 
         ), 
         child: child
      ), 
   ); 
}
  • Biên dịch và chạy ứng dụng để xem kết quả. Phiên bản đầu tiên và phiên bản cuối cùng của ứng dụng như sau:

Flutter cung cấp một khuôn khổ chung để truy cập tính năng cụ thể của nền tảng. Điều này cho phép nhà phát triển mở rộng chức năng của khung Flutter bằng cách sử dụng mã nền tảng cụ thể. Chức năng cụ thể của nền tảng như máy ảnh, mức pin, trình duyệt, v.v., có thể được truy cập dễ dàng thông qua khung.

Ý tưởng chung về việc truy cập mã cụ thể của nền tảng là thông qua giao thức nhắn tin đơn giản. Mã rung, Máy khách và mã nền tảng và Máy chủ liên kết với một Kênh thông báo chung. Máy khách gửi tin nhắn đến Máy chủ thông qua Kênh thông báo. Máy chủ lắng nghe trên Kênh thông báo, nhận thông báo và thực hiện các chức năng cần thiết và cuối cùng, trả kết quả cho Máy khách thông qua Kênh thông báo.

Kiến trúc mã nền tảng cụ thể được hiển thị trong sơ đồ khối dưới đây:

Giao thức nhắn tin sử dụng codec tin nhắn tiêu chuẩn (lớp StandardMessageCodec) hỗ trợ tuần tự hóa nhị phân các giá trị giống JSON như số, chuỗi, boolean, v.v., Tuần tự hóa và hủy tuần tự hóa hoạt động minh bạch giữa máy khách và máy chủ.

Hãy để chúng tôi viết một ứng dụng đơn giản để mở trình duyệt bằng Android SDK và hiểu cách

  • Tạo một ứng dụng Flutter mới trong Android studio, Flutter_browser_app

  • Thay thế mã main.dart bằng mã dưới đây -

import 'package:flutter/material.dart'; 
void main() => runApp(MyApp()); 
class MyApp extends StatelessWidget { 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage(title: 'Flutter Demo Home Page'),
      );
   }
}
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar(
            title: Text(this.title), 
         ), 
         body: Center(
            child: RaisedButton( 
               child: Text('Open Browser'), 
               onPressed: null, 
            ), 
         ), 
      ); 
   }
}
  • Ở đây, chúng tôi đã tạo một nút mới để mở trình duyệt và đặt phương thức onPressed của nó là null.

  • Bây giờ, hãy nhập các gói sau:

import 'dart:async'; 
import 'package:flutter/services.dart';
  • Ở đây, services.dart bao gồm chức năng gọi mã nền tảng cụ thể.

  • Tạo một kênh tin nhắn mới trong tiện ích MyHomePage.

static const platform = const 
MethodChannel('flutterapp.tutorialspoint.com/browser');
  • Viết một phương thức, _openBrowser để gọi phương thức nền tảng cụ thể, phương thức openBrowser thông qua kênh tin nhắn.

Future<void> _openBrowser() async { 
   try {
      final int result = await platform.invokeMethod(
         'openBrowser', <String, String>{ 
            'url': "https://flutter.dev" 
         }
      ); 
   } 
   on PlatformException catch (e) { 
      // Unable to open the browser 
      print(e); 
   }
}

Ở đây, chúng tôi đã sử dụng platform.invokeMethod để gọi openBrowser (được giải thích trong các bước tiếp theo). openBrowser có một đối số, url để mở một url cụ thể.

  • Thay đổi giá trị của thuộc tính onPressed của RaisedButton từ null thành _openBrowser.

onPressed: _openBrowser,
  • Mở MainActivity.java (bên trong thư mục android) và nhập thư viện cần thiết -

import android.app.Activity; 
import android.content.Intent; 
import android.net.Uri; 
import android.os.Bundle; 

import io.flutter.app.FlutterActivity; 
import io.flutter.plugin.common.MethodCall; 
import io.flutter.plugin.common.MethodChannel; 
import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 
import io.flutter.plugin.common.MethodChannel.Result; 
import io.flutter.plugins.GeneratedPluginRegistrant;
  • Viết phương thức, openBrowser để mở trình duyệt

private void openBrowser(MethodCall call, Result result, String url) { 
   Activity activity = this; 
   if (activity == null) { 
      result.error("ACTIVITY_NOT_AVAILABLE", 
      "Browser cannot be opened without foreground 
      activity", null); 
      return; 
   } 
   Intent intent = new Intent(Intent.ACTION_VIEW); 
   intent.setData(Uri.parse(url)); 
   
   activity.startActivity(intent); 
   result.success((Object) true); 
}
  • Bây giờ, đặt tên kênh trong lớp MainActivity -

private static final String CHANNEL = "flutterapp.tutorialspoint.com/browser";
  • Viết mã cụ thể cho android để đặt xử lý tin nhắn trong phương thức onCreate -

new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( 
   new MethodCallHandler() { 
   @Override 
   public void onMethodCall(MethodCall call, Result result) { 
      String url = call.argument("url"); 
      if (call.method.equals("openBrowser")) {
         openBrowser(call, result, url); 
      } else { 
         result.notImplemented(); 
      } 
   } 
});

Ở đây, chúng tôi đã tạo một kênh thông báo bằng cách sử dụng lớp MethodChannel và sử dụng lớp MethodCallHandler để xử lý thông báo. onMethodCall là phương thức thực tế chịu trách nhiệm gọi đúng mã nền tảng cụ thể bằng cách kiểm tra tin nhắn. Phương thức onMethodCall trích xuất url từ tin nhắn và sau đó chỉ gọi openBrowser khi lệnh gọi phương thức là openBrowser. Nếu không, nó trả về phương thức notImplemented.

Mã nguồn hoàn chỉnh của ứng dụng như sau:

main.dart

MainActivity.java

package com.tutorialspoint.flutterapp.flutter_browser_app; 

import android.app.Activity; 
import android.content.Intent; 
import android.net.Uri; 
import android.os.Bundle; 
import io.flutter.app.FlutterActivity; 
import io.flutter.plugin.common.MethodCall; 
import io.flutter.plugin.common.MethodChannel.Result; 
import io.flutter.plugins.GeneratedPluginRegistrant; 

public class MainActivity extends FlutterActivity { 
   private static final String CHANNEL = "flutterapp.tutorialspoint.com/browser"; 
   @Override 
   protected void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      GeneratedPluginRegistrant.registerWith(this); 
      new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
         new MethodCallHandler() {
            @Override 
            public void onMethodCall(MethodCall call, Result result) {
               String url = call.argument("url"); 
               if (call.method.equals("openBrowser")) { 
                  openBrowser(call, result, url); 
               } else { 
                  result.notImplemented(); 
               }
            }
         }
      ); 
   }
   private void openBrowser(MethodCall call, Result result, String url) {
      Activity activity = this; if (activity == null) {
         result.error(
            "ACTIVITY_NOT_AVAILABLE", "Browser cannot be opened without foreground activity", null
         ); 
         return; 
      } 
      Intent intent = new Intent(Intent.ACTION_VIEW); 
      intent.setData(Uri.parse(url)); 
      activity.startActivity(intent); 
      result.success((Object) true); 
   }
}

main.dart

import 'package:flutter/material.dart'; 
import 'dart:async'; 
import 'package:flutter/services.dart'; 

void main() => runApp(MyApp()); 
class MyApp extends StatelessWidget {
   @override 
   Widget build(BuildContext context) {
      return MaterialApp(
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage(
            title: 'Flutter Demo Home Page'
         ), 
      ); 
   }
}
class MyHomePage extends StatelessWidget {
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   static const platform = const MethodChannel('flutterapp.tutorialspoint.com/browser'); 
   Future<void> _openBrowser() async {
      try {
         final int result = await platform.invokeMethod('openBrowser', <String, String>{ 
            'url': "https://flutter.dev" 
         });
      }
      on PlatformException catch (e) { 
         // Unable to open the browser print(e); 
      } 
   }
   @override 
   Widget build(BuildContext context) {
      return Scaffold( 
         appBar: AppBar( 
            title: Text(this.title), 
         ), 
         body: Center(
            child: RaisedButton( 
               child: Text('Open Browser'), 
               onPressed: _openBrowser, 
            ), 
         ),
      );
   }
}

Chạy ứng dụng và nhấp vào nút Mở Trình duyệt và bạn có thể thấy rằng trình duyệt đã được khởi chạy. Ứng dụng Trình duyệt - Trang chủ như được hiển thị trong ảnh chụp màn hình ở đây -

Truy cập mã cụ thể trên iOS tương tự như trên nền tảng Android ngoại trừ việc nó sử dụng ngôn ngữ cụ thể của iOS - Objective-C hoặc Swift và iOS SDK. Mặt khác, khái niệm này giống với khái niệm của nền tảng Android.

Chúng ta hãy viết ứng dụng tương tự như trong chương trước cho nền tảng iOS.

  • Hãy để chúng tôi tạo một ứng dụng mới trong Android Studio (macOS), flay_browser_ios_app

  • Thực hiện theo các bước 2 - 6 như trong chương trước.

  • Khởi động XCode và nhấp vào File → Open

  • Chọn dự án xcode trong thư mục ios của dự án flamingo của chúng tôi.

  • Mở AppDelegate.m trong Runner → Runner path. Nó chứa đoạn mã sau:

#include "AppDelegate.h" 
#include "GeneratedPluginRegistrant.h" 
@implementation AppDelegate 

- (BOOL)application:(UIApplication *)application
   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      // [GeneratedPluginRegistrant registerWithRegistry:self];
      // Override point for customization after application launch.
      return [super application:application didFinishLaunchingWithOptions:launchOptions];
   } 
@end
  • Chúng tôi đã thêm một phương pháp, openBrowser để mở trình duyệt với url được chỉ định. Nó chấp nhận một đối số, url.

- (void)openBrowser:(NSString *)urlString { 
   NSURL *url = [NSURL URLWithString:urlString]; 
   UIApplication *application = [UIApplication sharedApplication]; 
   [application openURL:url]; 
}
  • Trong phương thức didFinishLaunchingWithOptions, hãy tìm bộ điều khiển và đặt nó trong biến bộ điều khiển.

FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
  • Trong phương thức didFinishLaunchingWithOptions, đặt kênh của trình duyệt là flutterapp.tutorialspoint.com/browse -

FlutterMethodChannel* browserChannel = [
   FlutterMethodChannel methodChannelWithName:
   @"flutterapp.tutorialspoint.com/browser" binaryMessenger:controller];
  • Tạo một biến, bản thân yếu và đặt lớp hiện tại -

__weak typeof(self) weakSelf = self;
  • Bây giờ, triển khai setMethodCallHandler. Gọi openBrowser bằng cách đối sánh call.method. Nhận url bằng cách gọi call.arguments và chuyển nó trong khi gọi openBrowser.

[browserChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
   if ([@"openBrowser" isEqualToString:call.method]) { 
      NSString *url = call.arguments[@"url"];   
      [weakSelf openBrowser:url]; 
   } else { result(FlutterMethodNotImplemented); } 
}];
  • Mã hoàn chỉnh như sau:

#include "AppDelegate.h" 
#include "GeneratedPluginRegistrant.h" 
@implementation AppDelegate 

- (BOOL)application:(UIApplication *)application 
   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   
   // custom code starts 
   FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; 
   FlutterMethodChannel* browserChannel = [
      FlutterMethodChannel methodChannelWithName:
      @"flutterapp.tutorialspoint.com /browser" binaryMessenger:controller]; 
   
   __weak typeof(self) weakSelf = self; 
   [browserChannel setMethodCallHandler:^(
      FlutterMethodCall* call, FlutterResult result) { 
      
      if ([@"openBrowser" isEqualToString:call.method]) { 
         NSString *url = call.arguments[@"url"];
         [weakSelf openBrowser:url]; 
      } else { result(FlutterMethodNotImplemented); } 
   }]; 
   // custom code ends 
   [GeneratedPluginRegistrant registerWithRegistry:self]; 
   
   // Override point for customization after application launch. 
   return [super application:application didFinishLaunchingWithOptions:launchOptions]; 
}
- (void)openBrowser:(NSString *)urlString { 
   NSURL *url = [NSURL URLWithString:urlString]; 
   UIApplication *application = [UIApplication sharedApplication]; 
   [application openURL:url]; 
} 
@end
  • Mở thiết lập dự án.

  • Đi đến Capabilities và kích hoạt Background Modes.

  • Thêm vào *Background fetchRemote Notification**.

  • Bây giờ, hãy chạy ứng dụng. Nó hoạt động tương tự như phiên bản Android nhưng trình duyệt Safari sẽ được mở thay vì chrome.

Cách sắp xếp và chia sẻ một bộ chức năng của Dart là thông qua Gói. Gói Dart chỉ đơn giản là các thư viện hoặc mô-đun có thể chia sẻ. Nhìn chung, Gói Dart giống như của Ứng dụng Phi tiêu ngoại trừ Gói Phi tiêu không có điểm nhập ứng dụng, chính.

Cấu trúc chung của Gói (xem xét gói demo, my_demo_package) như sau:

  • lib/src/* - Các tệp mã Dart riêng.

  • lib/my_demo_package.dart- Tệp mã Dart chính. Nó có thể được nhập vào một ứng dụng dưới dạng:

import 'package:my_demo_package/my_demo_package.dart'
  • Tệp mã riêng tư khác có thể được xuất thành tệp mã chính (my_demo_package.dart), nếu cần như hình dưới đây -

export src/my_private_code.dart
  • lib/*- Bất kỳ số lượng tệp mã Dart nào được sắp xếp trong bất kỳ cấu trúc thư mục tùy chỉnh nào. Mã có thể được truy cập như,

import 'package:my_demo_package/custom_folder/custom_file.dart'
  • pubspec.yaml - Đặc điểm kỹ thuật của dự án, giống như đặc điểm của ứng dụng,

Tất cả các tệp mã Dart trong Gói chỉ đơn giản là các lớp Dart và nó không có bất kỳ yêu cầu đặc biệt nào đối với mã Dart để đưa nó vào Gói.

Các loại gói

Vì các Gói Dart về cơ bản là một tập hợp nhỏ có chức năng tương tự, nó có thể được phân loại dựa trên chức năng của nó.

Gói phi tiêu

Mã Dart chung, có thể được sử dụng trong cả môi trường web và di động. Ví dụ: english_words là một gói như vậy chứa khoảng 5000 từ và có các chức năng tiện ích cơ bản như danh từ (liệt kê danh từ trong tiếng Anh), âm tiết (chỉ định số lượng âm tiết trong một từ.

Gói Flutter

Mã Generic Dart, phụ thuộc vào khuôn khổ Flutter và chỉ có thể được sử dụng trong môi trường di động. Ví dụ, fluro là một bộ định tuyến tùy chỉnh cho rung. Nó phụ thuộc vào khung Flutter.

Plugin Flutter

Mã Generic Dart, phụ thuộc vào khung Flutter cũng như mã nền tảng bên dưới (SDK Android hoặc SDK iOS). Ví dụ: camera là một plugin để tương tác với camera của thiết bị. Nó phụ thuộc vào khung Flutter cũng như khung cơ bản để truy cập vào camera.

Sử dụng gói phi tiêu

Gói Dart được lưu trữ và xuất bản vào máy chủ trực tiếp, https://pub.dartlang.org.Ngoài ra, Flutter cung cấp công cụ đơn giản, quán rượu để quản lý các Gói Dart trong ứng dụng. Các bước cần thiết để sử dụng dưới dạng Gói như sau:

  • Bao gồm tên gói và phiên bản cần thiết vào pubspec.yaml như hình dưới đây -

dependencies: english_words: ^3.1.5
  • Số phiên bản mới nhất có thể được tìm thấy bằng cách kiểm tra máy chủ trực tuyến.

  • Cài đặt gói vào ứng dụng bằng cách sử dụng lệnh sau:

flutter packages get
  • Trong khi phát triển trong Android studio, Android Studio sẽ phát hiện bất kỳ thay đổi nào trong pubspec.yaml và hiển thị cảnh báo gói Android studio cho nhà phát triển như được hiển thị bên dưới:

  • Gói Dart có thể được cài đặt hoặc cập nhật trong Android Studio bằng cách sử dụng các tùy chọn menu.

  • Nhập tệp cần thiết bằng lệnh hiển thị bên dưới và bắt đầu làm việc -

import 'package:english_words/english_words.dart';
  • Sử dụng bất kỳ phương pháp nào có sẵn trong gói,

nouns.take(50).forEach(print);
  • Ở đây, chúng tôi đã sử dụng hàm danh từ để lấy và in ra 50 từ hàng đầu.

Phát triển gói plugin Flutter

Phát triển một Plugin Flutter tương tự như phát triển một ứng dụng Dart hoặc Gói Dart. Ngoại lệ duy nhất là plugin sẽ sử dụng API hệ thống (Android hoặc iOS) để có được chức năng cần thiết cho nền tảng cụ thể.

Như chúng ta đã tìm hiểu cách truy cập mã nền tảng trong các chương trước, hãy để chúng tôi phát triển một plugin đơn giản, my_browser để hiểu quy trình phát triển plugin. Chức năng của plugin my_browser là cho phép ứng dụng mở trang web nhất định trong trình duyệt dành riêng cho nền tảng.

  • Khởi động Android Studio.

  • Nhấp chuột File → New Flutter Project và chọn tùy chọn Flutter Plugin.

  • Bạn có thể thấy một cửa sổ lựa chọn plugin Flutter như được hiển thị ở đây -

  • Nhập my_browser làm tên dự án và nhấp vào Tiếp theo.

  • Nhập tên plugin và các chi tiết khác vào cửa sổ như được hiển thị ở đây -

  • Nhập tên miền công ty, flopplugins.tutorialspoint.com trong cửa sổ hiển thị bên dưới và sau đó nhấp vào Finish. Nó sẽ tạo mã khởi động để phát triển plugin mới của chúng tôi.

  • Mở tệp my_browser.dart và viết một phương thức, openBrowser để gọi phương thức openBrowser cụ thể của nền tảng.

Future<void> openBrowser(String urlString) async { 
   try {
      final int result = await _channel.invokeMethod(
         'openBrowser', <String, String>{ 'url': urlString }
      );
   }
   on PlatformException catch (e) { 
      // Unable to open the browser print(e); 
   } 
}
  • Mở tệp MyBrowserPlugin.java và nhập các lớp sau:

import android.app.Activity; 
import android.content.Intent; 
import android.net.Uri; 
import android.os.Bundle;
  • Ở đây, chúng tôi phải nhập thư viện cần thiết để mở trình duyệt từ Android.

  • Thêm biến riêng mới mRegistrar thuộc loại Nhà đăng ký trong lớp MyBrowserPlugin.

private final Registrar mRegistrar;
  • Ở đây, Nhà đăng ký được sử dụng để lấy thông tin ngữ cảnh của mã gọi.

  • Thêm một phương thức khởi tạo để đặt Nhà đăng ký trong lớp MyBrowserPlugin.

private MyBrowserPlugin(Registrar registrar) { 
   this.mRegistrar = registrar; 
}
  • Thay đổi registerVới để bao gồm hàm tạo mới của chúng tôi trong lớp MyBrowserPlugin.

public static void registerWith(Registrar registrar) { 
   final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_browser"); 
   MyBrowserPlugin instance = new MyBrowserPlugin(registrar); 
   channel.setMethodCallHandler(instance); 
}
  • Thay đổi onMethodCall để bao gồm phương thức openBrowser trong lớp MyBrowserPlugin.

@Override 
public void onMethodCall(MethodCall call, Result result) { 
   String url = call.argument("url");
   if (call.method.equals("getPlatformVersion")) { 
      result.success("Android " + android.os.Build.VERSION.RELEASE); 
   } 
   else if (call.method.equals("openBrowser")) { 
      openBrowser(call, result, url); 
   } else { 
      result.notImplemented(); 
   } 
}
  • Viết phương thức openBrowser dành riêng cho nền tảng để truy cập trình duyệt trong lớp MyBrowserPlugin.

private void openBrowser(MethodCall call, Result result, String url) { 
   Activity activity = mRegistrar.activity(); 
   if (activity == null) {
      result.error("ACTIVITY_NOT_AVAILABLE", 
      "Browser cannot be opened without foreground activity", null); 
      return; 
   } 
   Intent intent = new Intent(Intent.ACTION_VIEW); 
   intent.setData(Uri.parse(url)); 
   activity.startActivity(intent); 
   result.success((Object) true); 
}
  • Mã nguồn hoàn chỉnh của plugin my_browser như sau:

my_browser.dart

import 'dart:async'; 
import 'package:flutter/services.dart'; 

class MyBrowser {
   static const MethodChannel _channel = const MethodChannel('my_browser'); 
   static Future<String> get platformVersion async { 
      final String version = await _channel.invokeMethod('getPlatformVersion'); return version; 
   } 
   Future<void> openBrowser(String urlString) async { 
      try {
         final int result = await _channel.invokeMethod(
            'openBrowser', <String, String>{'url': urlString}); 
      } 
      on PlatformException catch (e) { 
         // Unable to open the browser print(e); 
      }
   }
}

MyBrowserPlugin.java

package com.tutorialspoint.flutterplugins.my_browser; 

import io.flutter.plugin.common.MethodCall; 
import io.flutter.plugin.common.MethodChannel; 
import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 
import io.flutter.plugin.common.MethodChannel.Result; 
import io.flutter.plugin.common.PluginRegistry.Registrar; 
import android.app.Activity; 
import android.content.Intent; 
import android.net.Uri; 
import android.os.Bundle; 

/** MyBrowserPlugin */ 
public class MyBrowserPlugin implements MethodCallHandler {
   private final Registrar mRegistrar; 
   private MyBrowserPlugin(Registrar registrar) { 
      this.mRegistrar = registrar; 
   } 
   /** Plugin registration. */
   public static void registerWith(Registrar registrar) {
      final MethodChannel channel = new MethodChannel(
         registrar.messenger(), "my_browser"); 
      MyBrowserPlugin instance = new MyBrowserPlugin(registrar); 
      channel.setMethodCallHandler(instance); 
   } 
   @Override 
   public void onMethodCall(MethodCall call, Result result) { 
      String url = call.argument("url"); 
      if (call.method.equals("getPlatformVersion")) { 
         result.success("Android " + android.os.Build.VERSION.RELEASE); 
      } 
      else if (call.method.equals("openBrowser")) { 
         openBrowser(call, result, url); 
      } else { 
         result.notImplemented(); 
      } 
   } 
   private void openBrowser(MethodCall call, Result result, String url) { 
      Activity activity = mRegistrar.activity(); 
      if (activity == null) {
         result.error("ACTIVITY_NOT_AVAILABLE",
            "Browser cannot be opened without foreground activity", null); 
         return; 
      }
      Intent intent = new Intent(Intent.ACTION_VIEW); 
      intent.setData(Uri.parse(url)); 
      activity.startActivity(intent); 
      result.success((Object) true); 
   } 
}
  • Tạo một dự án mới, my_browser_plugin_test để kiểm tra plugin mới tạo của chúng tôi.

  • Mở pubspec.yaml và đặt my_browser làm phần phụ thuộc plugin.

dependencies: 
   flutter: 
      sdk: flutter 
   my_browser: 
      path: ../my_browser
  • Android studio sẽ thông báo rằng pubspec.yaml được cập nhật như được hiển thị trong cảnh báo gói Android studio được cung cấp bên dưới:

  • Nhấp vào Nhận tùy chọn phụ thuộc. Android studio sẽ lấy gói từ Internet và định cấu hình đúng cách cho ứng dụng.

  • Mở main.dart và bao gồm plugin my_browser như bên dưới:

import 'package:my_browser/my_browser.dart';
  • Gọi hàm openBrowser từ plugin my_browser như hình dưới đây -

onPressed: () => MyBrowser().openBrowser("https://flutter.dev"),
  • Mã hoàn chỉnh của main.dart như sau:

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

void main() => runApp(MyApp()); 

class MyApp extends StatelessWidget { 
   @override 
   Widget build(BuildContext context) {
      return MaterialApp( 
         title: 'Flutter Demo', 
         theme: ThemeData( 
            primarySwatch: Colors.blue, 
         ), 
         home: MyHomePage(
            title: 'Flutter Demo Home Page'
         ), 
      );,
   }
} 
class MyHomePage extends StatelessWidget { 
   MyHomePage({Key key, this.title}) : super(key: key); 
   final String title; 
   @override 
   Widget build(BuildContext context) {
      return Scaffold(
         appBar: AppBar( 
            title: Text(this.title), 
         ), 
         body: Center(
            child: RaisedButton(
               child: Text('Open Browser'), 
               onPressed: () => MyBrowser().openBrowser("https://flutter.dev"), 
            ),
         ), 
      ); 
   }
}
  • Chạy ứng dụng và nhấp vào nút Mở Trình duyệt và thấy rằng trình duyệt được khởi chạy. Bạn có thể thấy một ứng dụng Trình duyệt - Trang chủ như được hiển thị trong ảnh chụp màn hình bên dưới -

Bạn có thể thấy một ứng dụng Trình duyệt - Màn hình trình duyệt như trong ảnh chụp màn hình bên dưới -

  • Print
  •  Thêm ghi chú
  •  Đánh dấu trang này
  •  Báo cáo lỗi
  •  Suggestions

Lưu Đóng