Flutter - คู่มือฉบับย่อ
โดยทั่วไปการพัฒนาแอปพลิเคชันมือถือเป็นงานที่ซับซ้อนและท้าทาย มีกรอบมากมายสำหรับพัฒนาแอปพลิเคชันมือถือ Android มีเฟรมเวิร์กดั้งเดิมที่ใช้ภาษา Java และ iOS จัดเตรียมเฟรมเวิร์กดั้งเดิมตามภาษา Objective-C / Swift
อย่างไรก็ตามในการพัฒนาแอปพลิเคชันที่รองรับระบบปฏิบัติการทั้งสองระบบเราจำเป็นต้องเขียนโค้ดในภาษาที่แตกต่างกันสองภาษาโดยใช้สองกรอบงานที่แตกต่างกัน เพื่อช่วยเอาชนะความซับซ้อนนี้มีเฟรมเวิร์กมือถือที่รองรับทั้ง OS เฟรมเวิร์กเหล่านี้มีตั้งแต่เฟรมเวิร์กแอปพลิเคชันมือถือไฮบริดที่ใช้ HTML อย่างง่าย (ซึ่งใช้ HTML สำหรับอินเทอร์เฟซผู้ใช้และ JavaScript สำหรับลอจิกของแอปพลิเคชัน) ไปจนถึงเฟรมเวิร์กเฉพาะภาษาที่ซับซ้อน โดยไม่คำนึงถึงความเรียบง่ายหรือความซับซ้อนกรอบเหล่านี้มักจะมีข้อเสียมากมายหนึ่งในข้อเสียเปรียบหลักคือประสิทธิภาพที่ช้า
ในสถานการณ์นี้ Flutter ซึ่งเป็นเฟรมเวิร์กที่เรียบง่ายและมีประสิทธิภาพสูงซึ่งใช้ภาษา Dart จะให้ประสิทธิภาพสูงโดยการแสดงผล UI โดยตรงในพื้นที่ทำงานของระบบปฏิบัติการแทนที่จะใช้เฟรมเวิร์กดั้งเดิม
Flutter ยังมีวิดเจ็ตที่พร้อมใช้งาน (UI) มากมายเพื่อสร้างแอปพลิเคชันที่ทันสมัย วิดเจ็ตเหล่านี้ได้รับการปรับให้เหมาะสมสำหรับสภาพแวดล้อมมือถือและการออกแบบแอปพลิเคชันโดยใช้วิดเจ็ตนั้นง่ายพอ ๆ กับการออกแบบ HTML
เพื่อให้เฉพาะเจาะจงแอปพลิเคชั่น Flutter เป็นวิดเจ็ตเอง วิดเจ็ต Flutter ยังรองรับภาพเคลื่อนไหวและท่าทาง ตรรกะของแอปพลิเคชันขึ้นอยู่กับการเขียนโปรแกรมปฏิกิริยา วิดเจ็ตอาจมีสถานะเป็นทางเลือก ด้วยการเปลี่ยนสถานะของวิดเจ็ต Flutter จะทำการเปรียบเทียบสถานะของวิดเจ็ต (เก่าและใหม่) โดยอัตโนมัติและแสดงผลวิดเจ็ตด้วยการเปลี่ยนแปลงที่จำเป็นเท่านั้นแทนที่จะแสดงผลวิดเจ็ตใหม่ทั้งหมด
เราจะพูดถึงสถาปัตยกรรมที่สมบูรณ์ในบทต่อ ๆ ไป
คุณสมบัติของ Flutter
Flutter framework มีคุณสมบัติดังต่อไปนี้สำหรับนักพัฒนา -
กรอบที่ทันสมัยและมีปฏิกิริยา
ใช้ภาษาโปรแกรม Dart และเรียนรู้ได้ง่ายมาก
การพัฒนาที่รวดเร็ว
ส่วนต่อประสานผู้ใช้ที่สวยงามและลื่นไหล
แคตตาล็อกวิดเจ็ตขนาดใหญ่
รัน UI เดียวกันสำหรับหลายแพลตฟอร์ม
แอปพลิเคชั่นประสิทธิภาพสูง
ข้อดีของ Flutter
Flutter มาพร้อมกับวิดเจ็ตที่สวยงามและปรับแต่งได้เพื่อประสิทธิภาพสูงและแอพพลิเคชั่นมือถือที่โดดเด่น ตอบสนองความต้องการและข้อกำหนดที่กำหนดเองทั้งหมด นอกจากนี้ Flutter ยังมีข้อดีอีกมากมายดังที่กล่าวไว้ด้านล่าง -
Dart มีที่เก็บชุดซอฟต์แวร์ขนาดใหญ่ซึ่งช่วยให้คุณสามารถขยายขีดความสามารถของแอปพลิเคชันของคุณได้
นักพัฒนาจำเป็นต้องเขียนโค้ดฐานเดียวสำหรับทั้งสองแอปพลิเคชัน (ทั้งแพลตฟอร์ม Android และ iOS) Flutterอาจจะขยายไปยังแพลตฟอร์มอื่น ๆ ด้วยในอนาคต
Flutter ต้องการการทดสอบที่น้อยกว่า เนื่องจากฐานรหัสเดียวจึงเพียงพอหากเราเขียนการทดสอบอัตโนมัติเพียงครั้งเดียวสำหรับทั้งสองแพลตฟอร์ม
ความเรียบง่ายของ Flutter ทำให้เป็นตัวเลือกที่ดีสำหรับการพัฒนาที่รวดเร็ว ความสามารถในการปรับแต่งและความสามารถในการขยายทำให้มีประสิทธิภาพมากยิ่งขึ้น
ด้วย Flutter นักพัฒนาสามารถควบคุมวิดเจ็ตและเลย์เอาต์ได้อย่างเต็มที่
Flutter นำเสนอเครื่องมือสำหรับนักพัฒนาที่ยอดเยี่ยมพร้อมการโหลดซ้ำที่น่าทึ่ง
ข้อเสียของ Flutter
แม้จะมีข้อดีหลายประการ แต่การกระพือปีกก็มีข้อเสียดังต่อไปนี้ -
เนื่องจากมีการเข้ารหัสเป็นภาษา Dart นักพัฒนาจึงจำเป็นต้องเรียนรู้ภาษาใหม่ (แม้ว่าจะเรียนรู้ได้ง่าย)
เฟรมเวิร์กสมัยใหม่พยายามแยกตรรกะและ UI ให้มากที่สุด แต่ใน Flutter อินเทอร์เฟซผู้ใช้และตรรกะจะผสมกัน เราสามารถเอาชนะสิ่งนี้ได้โดยใช้การเข้ารหัสอัจฉริยะและใช้โมดูลระดับสูงเพื่อแยกส่วนติดต่อผู้ใช้และตรรกะ
Flutter เป็นอีกหนึ่งเฟรมเวิร์กในการสร้างแอปพลิเคชันมือถือ นักพัฒนากำลังประสบปัญหาในการเลือกเครื่องมือพัฒนาที่เหมาะสมในกลุ่มที่มีประชากรจำนวนมาก
บทนี้จะแนะนำคุณตลอดการติดตั้ง Flutter บนเครื่องคอมพิวเตอร์ของคุณโดยละเอียด
การติดตั้งใน Windows
ในส่วนนี้ให้เราดูวิธีการติดตั้งFlutter SDKและข้อกำหนดในระบบ windows
Step 1 - ไปที่ URLhttps://flutter.dev/docs/get-started/install/windowsและดาวน์โหลด Flutter SDK ล่าสุด ตั้งแต่เดือนเมษายน 2019 เวอร์ชัน 1.2.1 และไฟล์เป็น flutter_windows_v1.2.1-stable.zip
Step 2 - คลายซิปไฟล์ zip ในโฟลเดอร์พูดว่า C: \ flutter \
Step 3 - อัปเดตเส้นทางระบบเพื่อรวมไดเรกทอรี flutter bin
Step 4 - Flutter เป็นเครื่องมือแพทย์เพื่อตรวจสอบความต้องการของการพัฒนา Flutter ทั้งหมด
flutter doctor
Step 5 - การรันคำสั่งดังกล่าวจะวิเคราะห์ระบบและแสดงรายงานดังที่แสดงด้านล่าง -
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.
รายงานระบุว่ามีเครื่องมือในการพัฒนาทั้งหมด แต่ไม่ได้เชื่อมต่ออุปกรณ์ เราสามารถแก้ไขได้โดยเชื่อมต่ออุปกรณ์ Android ผ่าน USB หรือเริ่มโปรแกรมจำลอง android
Step 6 - ติดตั้ง Android SDK ล่าสุดหากรายงานโดยแพทย์กระพือปีก
Step 7 - ติดตั้ง Android Studio ล่าสุดหากรายงานโดยแพทย์กระพือปีก
Step 8 - เริ่มโปรแกรมจำลอง Android หรือเชื่อมต่ออุปกรณ์ Android จริงกับระบบ
Step 9- ติดตั้งปลั๊กอิน Flutter and Dart สำหรับ Android Studio มีเทมเพลตเริ่มต้นเพื่อสร้างแอปพลิเคชัน Flutter ใหม่ตัวเลือกในการเรียกใช้และแก้ไขข้อบกพร่องแอปพลิเคชัน Flutter ในสตูดิโอ Android เป็นต้น
เปิด Android Studio
คลิกไฟล์→การตั้งค่า→ปลั๊กอิน
เลือกปลั๊กอิน Flutter แล้วคลิกติดตั้ง
คลิกใช่เมื่อได้รับแจ้งให้ติดตั้งปลั๊กอิน Dart
รีสตาร์ท Android Studio
การติดตั้งใน MacOS
ในการติดตั้ง Flutter บน MacOS คุณจะต้องทำตามขั้นตอนต่อไปนี้ -
Step 1 - ไปที่ URLhttps://flutter.dev/docs/get-started/install/macosและดาวน์โหลด Flutter SDK ล่าสุด ตั้งแต่เดือนเมษายน 2019 เวอร์ชัน 1.2.1 และไฟล์คือ flutter_macos_v1.2.1- stable.zip
Step 2 - คลายซิปไฟล์ zip ในโฟลเดอร์พูด / path / to / flutter
Step 3 - อัปเดตเส้นทางระบบเพื่อรวมไดเรกทอรี flutter bin (ในไฟล์ ~ / .bashrc)
> export PATH = "$PATH:/path/to/flutter/bin"
Step 4 - เปิดใช้งานเส้นทางที่อัปเดตในเซสชันปัจจุบันโดยใช้คำสั่งด้านล่างจากนั้นตรวจสอบด้วย
source ~/.bashrc
source $HOME/.bash_profile
echo $PATH
Flutter เป็นเครื่องมือแพทย์เพื่อตรวจสอบความต้องการของการพัฒนา Flutter ทั้งหมด มันคล้ายกับคู่ของ Windows
Step 5 - ติดตั้ง XCode ล่าสุดหากรายงานโดยแพทย์กระพือปีก
Step 6 - ติดตั้ง Android SDK ล่าสุดหากรายงานโดยแพทย์กระพือปีก
Step 7 - ติดตั้ง Android Studio ล่าสุดหากรายงานโดยแพทย์กระพือปีก
Step 8 - เริ่มโปรแกรมจำลอง Android หรือเชื่อมต่ออุปกรณ์ Android จริงเข้ากับระบบเพื่อพัฒนาแอปพลิเคชัน Android
Step 9 - เปิดโปรแกรมจำลอง iOS หรือเชื่อมต่ออุปกรณ์ iPhone จริงเข้ากับระบบเพื่อพัฒนาแอปพลิเคชัน iOS
Step 10- ติดตั้งปลั๊กอิน Flutter and Dart สำหรับ Android Studio มีเทมเพลตเริ่มต้นเพื่อสร้างแอปพลิเคชัน Flutter ใหม่ตัวเลือกในการเรียกใช้และแก้ไขข้อบกพร่องแอปพลิเคชัน Flutter ในสตูดิโอ Android เป็นต้น
เปิด Android Studio
คลิก Preferences → Plugins
เลือกปลั๊กอิน Flutter แล้วคลิกติดตั้ง
คลิกใช่เมื่อได้รับแจ้งให้ติดตั้งปลั๊กอิน Dart
รีสตาร์ท Android Studio
ในบทนี้ให้เราสร้างแอปพลิเคชันFlutterง่ายๆเพื่อทำความเข้าใจพื้นฐานของการสร้างแอปพลิเคชันFlutterใน Android Studio
Step 1 - เปิด Android Studio
Step 2- สร้างโครงการกระพือปีก สำหรับสิ่งนี้คลิกFile → New → New Flutter Project
Step 3- เลือก Flutter Application สำหรับสิ่งนี้ให้เลือกFlutter Application แล้วคลิก Next.
Step 4 - กำหนดค่าแอปพลิเคชันตามด้านล่างแล้วคลิก Next.
ชื่อโครงการ: hello_app
เส้นทาง Flutter SDK: <path_to_flutter_sdk>
ที่ตั้งโครงการ: <path_to_project_folder>
คำอธิบาย: Flutter based hello world application
Step 5 - กำหนดค่าโครงการ
ตั้งค่าโดเมนของ บริษัท เป็น flutterapp.tutorialspoint.com แล้วคลิก Finish.
Step 6 - ป้อนโดเมน บริษัท
Android Studio สร้างแอปพลิเคชัน Flutter ที่ทำงานได้เต็มรูปแบบโดยมีฟังก์ชันการทำงานน้อยที่สุด ให้เราตรวจสอบโครงสร้างของแอปพลิเคชันจากนั้นเปลี่ยนรหัสเพื่อทำงานของเรา
โครงสร้างของแอปพลิเคชันและวัตถุประสงค์มีดังนี้ -
ส่วนประกอบต่างๆของโครงสร้างของแอปพลิเคชันอธิบายไว้ที่นี่ -
android - สร้างซอร์สโค้ดอัตโนมัติเพื่อสร้างแอปพลิเคชัน Android
ios - สร้างซอร์สโค้ดอัตโนมัติเพื่อสร้างแอปพลิเคชั่น ios
lib - โฟลเดอร์หลักที่มีรหัส Dart ที่เขียนโดยใช้ flutter framework
ib/main.dart - จุดเริ่มต้นของแอปพลิเคชัน Flutter
test - โฟลเดอร์ที่มีรหัส Dart เพื่อทดสอบแอปพลิเคชัน Flutter
test/widget_test.dart - โค้ดตัวอย่าง
.gitignore - ไฟล์ควบคุมเวอร์ชัน Git
.metadata - สร้างโดยอัตโนมัติโดยเครื่องมือกระพือ
.packages - สร้างขึ้นโดยอัตโนมัติเพื่อติดตามแพ็คเกจกระพือปีก
.iml - ไฟล์โครงการที่ใช้โดย Android studio
pubspec.yaml - ใช้โดย Pub, ตัวจัดการแพ็คเกจกระพือปีก
pubspec.lock - สร้างโดยอัตโนมัติโดยตัวจัดการแพ็คเกจ Flutter Pub
README.md - ไฟล์คำอธิบายโครงการที่เขียนในรูปแบบ Markdown
Step 7- แทนที่รหัสโผในไฟล์lib / main.dartด้วยรหัสด้านล่าง -
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',
)
),
);
}
}
ให้เราเข้าใจรหัสโผทีละบรรทัด
Line 1- การนำเข้าแพคเกจกระพือ, วัสดุ วัสดุนี้เป็นแพ็คเกจที่กระพือปีกเพื่อสร้างอินเทอร์เฟซผู้ใช้ตามแนวทางการออกแบบ Material ที่ Android กำหนด
Line 3- นี่คือจุดเริ่มต้นของแอปพลิเคชัน Flutter เรียกใช้ฟังก์ชันrunAppและส่งผ่านวัตถุของคลาสMyApp วัตถุประสงค์ของฟังก์ชันrunAppคือการแนบวิดเจ็ตที่กำหนดเข้ากับหน้าจอ
Line 5-17- วิดเจ็ตใช้สร้าง UI ในเฟรมเวิร์กกระพือปีก StatelessWidgetเป็นวิดเจ็ตที่ไม่คงสถานะใด ๆ ของวิดเจ็ต MyAppขยายStatelessWidgetและแทนที่ของวิธีการสร้าง วัตถุประสงค์ของวิธีการสร้างคือการสร้างส่วนหนึ่งของ UI ของแอปพลิเคชัน ที่นี่วิธีการสร้างใช้MaterialAppซึ่งเป็นวิดเจ็ตในการสร้าง UI ระดับรากของแอปพลิเคชัน มันมีสามคุณสมบัติ - ชื่อธีมและบ้าน
titleคือชื่อของแอปพลิเคชัน
ธีมเป็นธีมของวิดเจ็ต ที่นี่เราตั้งสีฟ้าเป็นสีโดยรวมของแอพลิเคชันโดยใช้ThemeDataระดับและทรัพย์สินของprimarySwatch
หน้าแรกเป็น UI ภายในของแอปพลิเคชันซึ่งเราตั้งค่าวิดเจ็ตอื่น MyHomePage
Line 19 - 38- MyHomePageเหมือนกับMyAppยกเว้นจะส่งคืนScaffold Widget Scaffoldเป็นวิดเจ็ตระดับบนสุดถัดจากวิดเจ็ตMaterialApp ที่ใช้สร้าง UI ที่สอดคล้องกับดีไซน์ Material มีคุณสมบัติที่สำคัญสองประการคือappBarเพื่อแสดงส่วนหัวของแอปพลิเคชันและเนื้อหาเพื่อแสดงเนื้อหาจริงของแอปพลิเคชัน AppBarเป็นอีกหนึ่งวิดเจ็ตในการแสดงผลส่วนหัวของแอปพลิเคชันและเราได้ใช้มันในคุณสมบัติappBar ในคุณสมบัติของร่างกายเราได้ใช้วิดเจ็ตCenterซึ่งเป็นศูนย์กลางของวิดเจ็ตลูก ข้อความเป็นวิดเจ็ตขั้นสุดท้ายและอยู่ภายในที่สุดในการแสดงข้อความและจะแสดงอยู่ตรงกลางหน้าจอ
Step 8 - ตอนนี้เรียกใช้แอปพลิเคชันโดยใช้ Run → Run main.dart
Step 9 - สุดท้ายผลลัพธ์ของแอปพลิเคชันมีดังนี้ -
ในบทนี้ให้เราพูดถึงสถาปัตยกรรมของเฟรมเวิร์ก Flutter
วิดเจ็ต
แนวคิดหลักของ Flutter framework คือ In Flutter, Everything is a widget. วิดเจ็ตเป็นส่วนประกอบส่วนติดต่อผู้ใช้โดยทั่วไปที่ใช้ในการสร้างส่วนต่อประสานผู้ใช้ของแอปพลิเคชัน
ในFlutterแอปพลิเคชันเป็นวิดเจ็ต แอปพลิเคชันเป็นวิดเจ็ตระดับบนสุดและ UI ของมันถูกสร้างโดยใช้ลูก (วิดเจ็ต) หนึ่งลูกขึ้นไปซึ่งสร้างอีกครั้งโดยใช้วิดเจ็ตลูกของมัน นี้composability คุณลักษณะช่วยให้เราสร้างส่วนต่อประสานผู้ใช้ที่มีความซับซ้อน
ตัวอย่างเช่นลำดับชั้นวิดเจ็ตของแอปพลิเคชัน hello world (สร้างในบทก่อนหน้า) เป็นไปตามที่ระบุในแผนภาพต่อไปนี้ -
ประเด็นต่อไปนี้น่าสังเกต -
แอปของฉันเป็นผู้ที่สร้างเครื่องมือและมันเป็นสร้างโดยใช้เครื่องมือพื้นเมืองกระพือ, MaterialApp
MaterialAppมีคุณสมบัติที่บ้านเพื่อระบุส่วนติดต่อผู้ใช้ของหน้าบ้านซึ่งเป็นอีกครั้งที่ผู้ใช้สร้างเครื่องมือMyHomePage
MyHomePageสร้างโดยใช้วิดเจ็ตเนทีฟอื่นที่กระพือปีกScaffold
Scaffoldมีสองคุณสมบัติ - ร่างกายและappBar
bodyใช้เพื่อระบุส่วนติดต่อผู้ใช้หลักและappBarใช้เพื่อระบุส่วนติดต่อผู้ใช้ส่วนหัว
ส่วนหัว UIถูกสร้างโดยใช้วิดเจ็ตเนทีฟที่กระพือปีก AppBarและBody UIสร้างโดยใช้วิดเจ็ตCenter
ศูนย์เครื่องมือมีคุณสมบัติเด็กซึ่งหมายเนื้อหาจริงและเป็นที่สร้างโดยใช้ข้อความวิดเจ็ต
ท่าทาง
กระพือวิดเจ็ตปฏิสัมพันธ์ผ่านการสนับสนุนเครื่องมือพิเศษGestureDetector GestureDetectorเป็นวิดเจ็ตที่มองไม่เห็นซึ่งมีความสามารถในการจับการโต้ตอบของผู้ใช้เช่นการแตะการลาก ฯลฯ ของวิดเจ็ตลูก เครื่องมือพื้นเมืองจำนวนมากของการปฏิสัมพันธ์สนับสนุนกระพือผ่านการใช้GestureDetector นอกจากนี้เรายังสามารถรวมคุณลักษณะแบบโต้ตอบลงในวิดเจ็ตที่มีอยู่ได้ด้วยการเขียนด้วยวิดเจ็ตGestureDetector เราจะเรียนรู้ท่าทางแยกกันในบทที่กำลังจะมาถึง
แนวคิดของรัฐ
เครื่องมือ Flutter สนับสนุนการบำรุงรักษาของรัฐโดยการให้เครื่องมือพิเศษStatefulWidget Widget ความต้องการที่จะได้รับจากStatefulWidgetเครื่องมือที่จะบำรุงรักษารัฐสนับสนุนและเครื่องมืออื่น ๆ ที่ทุกคนควรจะได้รับจากStatefulWidget วิดเจ็ต Flutter คือreactiveในภาษาพื้นเมือง สิ่งนี้คล้ายกับ reactjs และStatefulWidgetจะแสดงผลอัตโนมัติเมื่อใดก็ตามที่สถานะภายในมีการเปลี่ยนแปลง การเรนเดอร์ได้รับการปรับให้เหมาะสมโดยการค้นหาความแตกต่างระหว่าง UI วิดเจ็ตเก่าและใหม่และการแสดงผลเฉพาะการเปลี่ยนแปลงที่จำเป็น
เลเยอร์
แนวคิดที่สำคัญที่สุดของ Flutter framework คือเฟรมเวิร์กถูกจัดกลุ่มออกเป็นหลายประเภทในแง่ของความซับซ้อนและจัดเรียงอย่างชัดเจนในชั้นของความซับซ้อนที่ลดลง เลเยอร์กำลังสร้างโดยใช้เลเยอร์ระดับถัดไปทันที ด้านบนชั้นส่วนใหญ่เป็นเครื่องมือที่เฉพาะเจาะจงกับAndroidและiOS เลเยอร์ถัดไปมีวิดเจ็ตเนทีฟที่กระพือปีกทั้งหมด เลเยอร์ถัดไปคือเลเยอร์การแสดงผลซึ่งเป็นส่วนประกอบของเรนเดอร์ระดับต่ำและแสดงผลทุกอย่างในแอปพลิเคชัน Flutter เลเยอร์ลงไปที่โค้ดเฉพาะแพลตฟอร์มหลัก
ภาพรวมทั่วไปของเลเยอร์ใน Flutter ระบุไว้ในแผนภาพด้านล่าง -
ประเด็นต่อไปนี้สรุปสถาปัตยกรรมของ Flutter -
ใน Flutter ทุกอย่างเป็นวิดเจ็ตและวิดเจ็ตที่ซับซ้อนประกอบด้วยวิดเจ็ตที่มีอยู่แล้ว
สามารถรวมคุณสมบัติแบบโต้ตอบได้ทุกเมื่อที่จำเป็นโดยใช้วิดเจ็ตGestureDetector
สามารถรักษาสถานะของวิดเจ็ตได้ทุกเมื่อที่จำเป็นโดยใช้วิดเจ็ตStatefulWidget
Flutter นำเสนอการออกแบบเลเยอร์เพื่อให้สามารถตั้งโปรแกรมเลเยอร์ใดก็ได้ขึ้นอยู่กับความซับซ้อนของงาน
เราจะพูดถึงแนวคิดทั้งหมดนี้โดยละเอียดในบทต่อ ๆ ไป
Dart เป็นภาษาโปรแกรมสำหรับวัตถุประสงค์ทั่วไปแบบโอเพนซอร์ส พัฒนาโดย Google Dart เป็นภาษาเชิงวัตถุที่มีไวยากรณ์รูปแบบ C รองรับแนวคิดการเขียนโปรแกรมเช่นอินเทอร์เฟซคลาสซึ่งแตกต่างจากภาษาโปรแกรมอื่น ๆ Dart ไม่รองรับอาร์เรย์ คอลเลกชัน Dart สามารถใช้เพื่อจำลองโครงสร้างข้อมูลเช่นอาร์เรย์ข้อมูลทั่วไปและการพิมพ์เสริม
รหัสต่อไปนี้แสดงโปรแกรม Dart อย่างง่าย -
void main() {
print("Dart language is easy to learn");
}
ตัวแปรและชนิดข้อมูล
ตัวแปรคือชื่อตำแหน่งที่จัดเก็บและประเภทข้อมูลหมายถึงประเภทและขนาดของข้อมูลที่เกี่ยวข้องกับตัวแปรและฟังก์ชัน
Dart ใช้คำสำคัญvarเพื่อประกาศตัวแปร ไวยากรณ์ของvarถูกกำหนดไว้ด้านล่าง
var name = 'Dart';
สุดท้ายและconstคำหลักที่จะใช้ในการคงประกาศ มีการกำหนดไว้ดังนี้ -
void main() {
final a = 12;
const pi = 3.14;
print(a);
print(pi);
}
ภาษา Dart รองรับประเภทข้อมูลต่อไปนี้ -
Numbers - ใช้เพื่อแสดงตัวอักษรตัวเลข - จำนวนเต็มและคู่
Strings- แสดงถึงลำดับของอักขระ ค่าสตริงถูกระบุในเครื่องหมายคำพูดเดี่ยวหรือคู่
Booleans- โผใช้boolคำหลักเพื่อแสดงค่าบูลีน - ความจริงและเท็จ
Lists and Maps- ใช้เพื่อแสดงคอลเล็กชันของวัตถุ สามารถกำหนดรายการอย่างง่ายได้ดังต่อไปนี้ -
void main() {
var list = [1,2,3,4,5];
print(list);
}
รายการที่แสดงด้านบนสร้าง [1,2,3,4,5] รายการ
สามารถกำหนดแผนที่ได้ดังแสดงที่นี่ -
void main() {
var mapping = {'id': 1,'name':'Dart'};
print(mapping);
}
Dynamic- หากไม่ได้กำหนดประเภทตัวแปรประเภทเริ่มต้นจะเป็นแบบไดนามิก ตัวอย่างต่อไปนี้แสดงตัวแปรชนิดไดนามิก -
void main() {
dynamic name = "Dart";
print(name);
}
การตัดสินใจและการวนซ้ำ
บล็อกการตัดสินใจจะประเมินเงื่อนไขก่อนดำเนินการตามคำสั่ง Dart สนับสนุน If, If..else และเปลี่ยนคำสั่ง
ลูปใช้เพื่อทำซ้ำบล็อกโค้ดจนกว่าจะตรงตามเงื่อนไขที่กำหนด Dart สนับสนุน for..in, while and do .. while ลูป
ให้เราเข้าใจตัวอย่างง่ายๆเกี่ยวกับการใช้คำสั่งควบคุมและลูป -
void main() {
for( var i = 1 ; i <= 10; i++ ) {
if(i%2==0) {
print(i);
}
}
}
รหัสด้านบนจะพิมพ์เลขคู่ตั้งแต่ 1 ถึง 10
ฟังก์ชั่น
ฟังก์ชันคือกลุ่มของคำสั่งที่ร่วมกันดำเนินงานเฉพาะ ให้เราดูฟังก์ชันง่ายๆใน Dart ดังที่แสดงไว้ที่นี่ -
void main() {
add(3,4);
}
void add(int a,int b) {
int c;
c = a+b;
print(c);
}
ฟังก์ชันข้างต้นจะเพิ่มค่าสองค่าและสร้าง 7 เป็นเอาต์พุต
การเขียนโปรแกรมเชิงวัตถุ
Dart เป็นภาษาเชิงวัตถุ รองรับคุณสมบัติการเขียนโปรแกรมเชิงวัตถุเช่นคลาสอินเทอร์เฟซ ฯลฯ
คลาสเป็นพิมพ์เขียวสำหรับสร้างวัตถุ คำจำกัดความของคลาสมีดังต่อไปนี้ -
- Fields
- Getters และ setters
- Constructors
- Functions
ตอนนี้ให้เราสร้างคลาสง่ายๆโดยใช้คำจำกัดความข้างต้น -
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
}
ดังที่เราได้เรียนรู้ในบทก่อนหน้านี้วิดเจ็ตเป็นทุกอย่างในเฟรมเวิร์กของ Flutter เราได้เรียนรู้วิธีสร้างวิดเจ็ตใหม่ในบทก่อนหน้าแล้ว
ในบทนี้ให้เราเข้าใจแนวคิดที่แท้จริงเบื้องหลังการสร้างวิดเจ็ตและวิดเจ็ตประเภทต่างๆที่มีอยู่ในเฟรมเวิร์กFlutter
ให้เราตรวจสอบวิดเจ็ตMyHomePageของแอปพลิเคชันHello World รหัสสำหรับวัตถุประสงค์นี้มีดังต่อไปนี้ -
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',)),
);
}
}
ที่นี่เราได้สร้างเครื่องมือใหม่โดยการขยายStatelessWidget
โปรดสังเกตว่าStatelessWidgetต้องการเพียงวิธีการสร้างเดียวที่จะนำไปใช้ในคลาสที่ได้รับมา สร้างวิธีการรับสภาพแวดล้อมบริบทที่จำเป็นในการสร้างเครื่องมือที่ผ่านการBuildContextพารามิเตอร์และส่งกลับวิดเจ็ตมันสร้าง
ในโค้ดเราได้ใช้titleเป็นหนึ่งในอาร์กิวเมนต์ตัวสร้างและยังใช้คีย์เป็นอาร์กิวเมนต์อื่น ชื่อที่ใช้ในการแสดงชื่อและที่สำคัญจะใช้เพื่อระบุวิดเจ็ตในการสร้างสภาพแวดล้อม
ที่นี่วิธีการสร้างเรียกวิธีการสร้างของScaffoldซึ่งจะเรียกวิธีการสร้างของAppBarและCenterเพื่อสร้างส่วนต่อประสานผู้ใช้
สุดท้ายวิธีการสร้างศูนย์เรียกวิธีการสร้างข้อความ
เพื่อความเข้าใจที่ดีขึ้นการแสดงภาพของสิ่งเดียวกันจะได้รับด้านล่าง -
การสร้างภาพวิดเจ็ต
ในFlutterวิดเจ็ตสามารถแบ่งออกเป็นหลายประเภทตามคุณสมบัติดังที่แสดงด้านล่าง -
- วิดเจ็ตเฉพาะแพลตฟอร์ม
- วิดเจ็ตเค้าโครง
- วิดเจ็ตการบำรุงรักษาสถานะ
- วิดเจ็ตอิสระ / พื้นฐานของแพลตฟอร์ม
ให้เราคุยรายละเอียดแต่ละเรื่องในตอนนี้
วิดเจ็ตเฉพาะแพลตฟอร์ม
Flutter มีวิดเจ็ตเฉพาะสำหรับแพลตฟอร์มเฉพาะ - Android หรือ iOS
วิดเจ็ตเฉพาะของ Android ได้รับการออกแบบตามแนวทางการออกแบบ Materialโดย Android OS เครื่องมือเฉพาะ Android เรียกว่าเป็นเครื่องมือวัสดุ
วิดเจ็ตเฉพาะของ iOS ได้รับการออกแบบตามแนวทางการเชื่อมต่อของมนุษย์โดย Apple และเรียกว่าวิดเจ็ตCupertino
วิดเจ็ตวัสดุที่ใช้มากที่สุดมีดังนี้ -
- Scaffold
- AppBar
- BottomNavigationBar
- TabBar
- TabBarView
- ListTile
- RaisedButton
- FloatingActionButton
- FlatButton
- IconButton
- DropdownButton
- PopupMenuButton
- ButtonBar
- TextField
- Checkbox
- Radio
- Switch
- Slider
- ตัวเลือกวันที่และเวลา
- SimpleDialog
- AlertDialog
วิดเจ็ตCupertino ที่ใช้มากที่สุดมีดังนี้ -
- CupertinoButton
- CupertinoPicker
- CupertinoDatePicker
- CupertinoTimerPicker
- CupertinoNavigationBar
- CupertinoTabBar
- CupertinoTabScaffold
- CupertinoTabView
- CupertinoTextField
- CupertinoDialog
- CupertinoDialogAction
- CupertinoFullscreenDialogTransition
- CupertinoPageScaffold
- CupertinoPageTransition
- CupertinoActionSheet
- CupertinoActivityIndicator
- CupertinoAlertDialog
- CupertinoPopupSurface
- CupertinoSlider
วิดเจ็ตเค้าโครง
ใน Flutter วิดเจ็ตสามารถสร้างได้โดยการเขียนวิดเจ็ตตั้งแต่หนึ่งวิดเจ็ตขึ้นไป ในการสร้างวิดเจ็ตหลายรายการให้เป็นวิดเจ็ตเดียวFlutterมีวิดเจ็ตจำนวนมากพร้อมคุณสมบัติเค้าโครง ตัวอย่างเช่นวิดเจ็ตลูกสามารถอยู่กึ่งกลางโดยใช้วิดเจ็ตCenter
วิดเจ็ตเค้าโครงยอดนิยมบางส่วนมีดังนี้ -
Container- กล่องสี่เหลี่ยมตกแต่งโดยใช้วิดเจ็ตBoxDecorationพร้อมพื้นหลังเส้นขอบและเงา
Center - ตั้งศูนย์วิดเจ็ตลูก
Row - จัดเด็กในแนวนอน
Column - จัดเด็กในแนวตั้ง
Stack - จัดเรียงไว้ด้านบนอีกอัน
เราจะตรวจสอบเครื่องมือรูปแบบในรายละเอียดในที่จะเกิดขึ้นรู้เบื้องต้นเกี่ยวกับรูปแบบวิดเจ็ตบท
วิดเจ็ตการบำรุงรักษาสถานะ
ในกระพือ, วิดเจ็ตทั้งหมดจะได้มาทั้งจากStatelessWidgetหรือStatefulWidget
วิดเจ็ตที่ได้มาจากStatelessWidgetไม่ได้มีข้อมูลใด ๆ ของรัฐ แต่มันอาจจะมีมาจากเครื่องมือStatefulWidget ลักษณะไดนามิกของแอปพลิเคชันเกิดจากพฤติกรรมแบบโต้ตอบของวิดเจ็ตและสถานะจะเปลี่ยนไประหว่างการโต้ตอบ ตัวอย่างเช่นการแตะปุ่มตัวนับจะเพิ่ม / ลดสถานะภายในของตัวนับทีละรายการและลักษณะการตอบสนองของวิดเจ็ตFlutterจะแสดงวิดเจ็ตใหม่โดยอัตโนมัติโดยใช้ข้อมูลสถานะใหม่
เราจะได้เรียนรู้แนวคิดของStatefulWidgetเครื่องมือในรายละเอียดในที่จะเกิดขึ้นบทการจัดการของรัฐ
วิดเจ็ตอิสระ / พื้นฐานของแพลตฟอร์ม
Flutterมีวิดเจ็ตพื้นฐานจำนวนมากเพื่อสร้างส่วนต่อประสานผู้ใช้ที่เรียบง่ายและซับซ้อนในรูปแบบที่ไม่ขึ้นกับแพลตฟอร์ม ให้เราดูวิดเจ็ตพื้นฐานบางส่วนในบทนี้
Text
วิดเจ็ตข้อความใช้เพื่อแสดงสตริง สไตล์ของสตริงสามารถกำหนดได้โดยใช้คุณสมบัติสไตล์และคลาสTextStyle โค้ดตัวอย่างสำหรับวัตถุประสงค์นี้มีดังนี้ -
Text('Hello World!', style: TextStyle(fontWeight: FontWeight.bold))
วิดเจ็ตข้อความมีตัวสร้างพิเศษText.richซึ่งยอมรับลูกของประเภทTextSpanเพื่อระบุสตริงด้วยสไตล์ที่แตกต่างกัน วิดเจ็ตTextSpanเป็นแบบวนซ้ำและยอมรับTextSpanเป็นลูกของมัน โค้ดตัวอย่างสำหรับวัตถุประสงค์นี้มีดังนี้ -
Text.rich(
TextSpan(
children: <TextSpan>[
TextSpan(text: "Hello ", style:
TextStyle(fontStyle: FontStyle.italic)),
TextSpan(text: "World", style:
TextStyle(fontWeight: FontWeight.bold)),
],
),
)
คุณสมบัติที่สำคัญที่สุดของวิดเจ็ตข้อความมีดังนี้ -
maxLines, int - จำนวนบรรทัดสูงสุดที่จะแสดง
overflow, TextOverFlow- ระบุวิธีจัดการภาพล้นโดยใช้คลาสTextOverFlow
style, TextStyle- ระบุรูปแบบของสตริงโดยใช้คลาสTextStyle
textAlign, TextAlign- การจัดตำแหน่งข้อความเช่นขวาซ้ายจัดชิด ฯลฯ โดยใช้คลาสTextAlign
textDirection, TextDirection - ทิศทางของข้อความที่จะไหลจากซ้ายไปขวาหรือขวาไปซ้าย
Image
วิดเจ็ตรูปภาพใช้เพื่อแสดงภาพในแอปพลิเคชัน วิดเจ็ตรูปภาพมีตัวสร้างที่แตกต่างกันในการโหลดภาพจากหลายแหล่งและมีดังต่อไปนี้ -
Image- โปรแกรมโหลดรูปภาพทั่วไปโดยใช้ImageProvider
Image.asset - โหลดภาพจากทรัพย์สินของโครงการที่กระพือปีก
Image.file - โหลดภาพจากโฟลเดอร์ระบบ
Image.memory - โหลดภาพจากหน่วยความจำ
Image.Network - โหลดภาพจากเครือข่าย
ตัวเลือกที่ง่ายที่สุดในการโหลดและแสดงภาพในFlutterคือการรวมรูปภาพเป็นสินทรัพย์ของแอปพลิเคชันและโหลดลงในวิดเจ็ตตามความต้องการ
สร้างโฟลเดอร์เนื้อหาในโฟลเดอร์โครงการและวางภาพที่จำเป็น
ระบุเนื้อหาใน pubspec.yaml ตามที่แสดงด้านล่าง -
flutter:
assets:
- assets/smiley.png
ตอนนี้โหลดและแสดงภาพในแอปพลิเคชัน
Image.asset('assets/smiley.png')
ซอร์สโค้ดที่สมบูรณ์ของวิดเจ็ตMyHomePageของแอปพลิเคชัน hello world และผลลัพธ์จะเป็นดังที่แสดงด้านล่าง -
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")),
);
}
}
ภาพที่โหลดมีดังที่แสดงด้านล่าง -
คุณสมบัติที่สำคัญที่สุดของวิดเจ็ตรูปภาพมีดังนี้ -
image, ImageProvider - โหลดรูปภาพจริง
width, double - ความกว้างของภาพ
height, double - ความสูงของภาพ
alignment, AlignmentGeometry - วิธีจัดแนวภาพให้อยู่ในขอบเขต
Icon
วิดเจ็ตไอคอนใช้เพื่อแสดงสัญลักษณ์จากแบบอักษรที่อธิบายในคลาสIconData รหัสสำหรับโหลดไอคอนอีเมลง่ายๆมีดังนี้ -
Icon(Icons.email)
ซอร์สโค้ดที่สมบูรณ์เพื่อใช้ในแอปพลิเคชัน hello world มีดังต่อไปนี้ -
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)),
);
}
}
ไอคอนโหลดดังแสดงด้านล่าง -
ตั้งแต่แนวคิดหลักของการกระพือเป็นทุกสิ่งที่เป็นเครื่องมือ , Flutterรวมฟังก์ชันการทำงานรูปแบบส่วนติดต่อผู้ใช้เข้าไปในเครื่องมือของตัวเอง Flutterมีวิดเจ็ตที่ออกแบบมาเป็นพิเศษมากมายเช่นContainer, Center, Alignและอื่น ๆ เพื่อจุดประสงค์ในการจัดวางส่วนต่อประสานผู้ใช้เท่านั้น วิดเจ็ตสร้างโดยการเขียนวิดเจ็ตอื่น ๆ โดยปกติจะใช้วิดเจ็ตเค้าโครง ให้ใช้เรียนรู้แนวคิดเค้าโครงFlutterในบทนี้
ประเภทของวิดเจ็ตเค้าโครง
วิดเจ็ตเลย์เอาต์สามารถแบ่งออกเป็นสองหมวดหมู่ที่แตกต่างกันโดยขึ้นอยู่กับลูก -
- วิดเจ็ตรองรับลูกคนเดียว
- วิดเจ็ตรองรับเด็กหลายคน
ให้เราเรียนรู้ทั้งประเภทของวิดเจ็ตและการทำงานของวิดเจ็ตในส่วนต่อไป
วิดเจ็ตลูกเดียว
ในหมวดหมู่นี้วิดเจ็ตจะมีเพียงวิดเจ็ตลูกเดียวและทุกวิดเจ็ตจะมีฟังก์ชันการออกแบบพิเศษ
ตัวอย่างเช่นวิดเจ็ตCenterจะจัดให้วิดเจ็ตลูกเป็นศูนย์กลางในส่วนที่เกี่ยวกับวิดเจ็ตหลักและวิดเจ็ตคอนเทนเนอร์ให้ความยืดหยุ่นอย่างสมบูรณ์ในการวางลูกไว้ที่ตำแหน่งใดก็ได้ภายในโดยใช้ตัวเลือกที่แตกต่างกันเช่นการขยายการตกแต่ง
วิดเจ็ตลูกเดียวเป็นตัวเลือกที่ยอดเยี่ยมในการสร้างวิดเจ็ตคุณภาพสูงที่มีฟังก์ชันเดียวเช่นปุ่มป้ายกำกับ ฯลฯ
รหัสสำหรับสร้างปุ่มง่ายๆโดยใช้วิดเจ็ตคอนเทนเนอร์มีดังนี้ -
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)
),
),
);
}
}
ที่นี่เราได้ใช้สองวิดเจ็ต - วิดเจ็ตคอนเทนเนอร์และวิดเจ็ตข้อความ ผลลัพธ์ของวิดเจ็ตเป็นปุ่มแบบกำหนดเองดังที่แสดงด้านล่าง -
ให้เราตรวจสอบวิดเจ็ตเลย์เอาต์ลูกคนเดียวที่สำคัญที่สุดที่Flutterมีให้-
Padding- ใช้เพื่อจัดเรียงวิดเจ็ตลูกตามช่องว่างที่กำหนด ที่นี่สามารถจัดหาช่องว่างภายในได้โดยคลาสEdgeInsets
Align- จัดตำแหน่งวิดเจ็ตลูกภายในตัวเองโดยใช้ค่าของคุณสมบัติการจัดตำแหน่ง ค่าสำหรับคุณสมบัติการจัดตำแหน่งสามารถระบุได้โดยคลาสFractionalOffset FractionalOffsetชั้นระบุชดเชยในแง่ของระยะทางจากซ้ายด้านบน
ค่าออฟเซ็ตที่เป็นไปได้บางค่ามีดังนี้ -
FractionalOffset (1.0, 0.0) แสดงด้านขวาบน
FractionalOffset (0.0, 1.0) แทนค่าล่างซ้าย
โค้ดตัวอย่างเกี่ยวกับการชดเชยแสดงอยู่ด้านล่าง -
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 - ปรับขนาดวิดเจ็ตลูกแล้ววางตำแหน่งตามความพอดีที่ระบุ
AspectRatio - พยายามปรับขนาดวิดเจ็ตลูกตามอัตราส่วนภาพที่ระบุ
ConstrainedBox
Baseline
FractinallySizedBox
IntrinsicHeight
IntrinsicWidth
LiimitedBox
OffStage
OverflowBox
SizedBox
SizedOverflowBox
Transform
CustomSingleChildLayout
แอปพลิเคชัน hello world ของเราใช้วิดเจ็ตเค้าโครงตามวัสดุเพื่อออกแบบโฮมเพจ ให้เราปรับเปลี่ยนแอปพลิเคชัน hello world ของเราเพื่อสร้างโฮมเพจโดยใช้วิดเจ็ตเค้าโครงพื้นฐานตามที่ระบุด้านล่าง -
Container - วิดเจ็ตคอนเทนเนอร์แบบลูกเดียวแบบทั่วไปพร้อมการจัดตำแหน่งการขยายขอบและขอบพร้อมกับคุณสมบัติการจัดแต่งทรงผมที่หลากหลาย
Center - วิดเจ็ตคอนเทนเนอร์ลูกที่เรียบง่ายซึ่งเป็นศูนย์กลางของวิดเจ็ตลูก
รหัสแก้ไขของวิดเจ็ตMyHomePageและMyAppมีดังต่อไปนี้ -
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,
),
)
);
}
}
ที่นี่
วิดเจ็ตคอนเทนเนอร์คือวิดเจ็ตระดับบนสุดหรือรูท คอนเทนเนอร์ถูกกำหนดค่าโดยใช้คุณสมบัติการตกแต่งและการเติมเพื่อจัดวางเนื้อหา
BoxDecorationมีคุณสมบัติมากมายเช่นสีเส้นขอบ ฯลฯ ในการตกแต่งวิดเจ็ตคอนเทนเนอร์และที่นี่สีจะใช้เพื่อกำหนดสีของคอนเทนเนอร์
paddingของวิดเจ็ตContainerถูกตั้งค่าโดยใช้คลาสdgeInsetsซึ่งมีอ็อพชันเพื่อระบุค่าการขยาย
Centerเป็นวิดเจ็ตลูกของวิดเจ็ตContainer อีกครั้งTextเป็นลูกของวิดเจ็ตCenter ข้อความที่ใช้ในการแสดงข้อความและศูนย์จะใช้ในการศูนย์ข้อความที่เกี่ยวกับแม่เครื่องมือตู้คอนเทนเนอร์
ผลลัพธ์สุดท้ายของโค้ดที่ระบุข้างต้นคือตัวอย่างโครงร่างดังที่แสดงด้านล่าง -
วิดเจ็ตเด็กหลายรายการ
ในหมวดหมู่นี้วิดเจ็ตที่กำหนดจะมีวิดเจ็ตลูกมากกว่าหนึ่งรายการและรูปแบบของแต่ละวิดเจ็ตจะไม่ซ้ำกัน
ตัวอย่างเช่นวิดเจ็ตRowอนุญาตให้จัดวางเด็กในแนวนอนในขณะที่วิดเจ็ตคอลัมน์อนุญาตให้วางลูกในแนวตั้ง ด้วยการเขียนRowและColumnวิดเจ็ตที่มีระดับความซับซ้อนสามารถสร้างขึ้นได้
ให้เราเรียนรู้บางส่วนของวิดเจ็ตที่ใช้บ่อยในส่วนนี้
Row - อนุญาตให้จัดเรียงลูกในแนวนอน
Column - อนุญาตให้จัดเรียงลูกในแนวตั้ง
ListView - อนุญาตให้จัดเรียงลูกตามรายการ
GridView - อนุญาตให้จัดเด็กเป็นแกลเลอรี
Expanded - ใช้เพื่อทำให้ลูก ๆ ของวิดเจ็ต Row และ Column ครอบครองพื้นที่สูงสุดที่เป็นไปได้
Table - วิดเจ็ตตามตาราง
Flow - วิดเจ็ตตามกระแส
Stack - วิดเจ็ตตามกอง
แอปพลิเคชั่นเค้าโครงขั้นสูง
ในส่วนนี้ให้เราเรียนรู้วิธีสร้างอินเทอร์เฟซผู้ใช้ที่ซับซ้อนของรายการผลิตภัณฑ์ด้วยการออกแบบที่กำหนดเองโดยใช้วิดเจ็ตเค้าโครงลูกเดียวและหลายรายการ
เพื่อจุดประสงค์นี้ให้ทำตามลำดับด้านล่าง -
สร้างใหม่Flutterการประยุกต์ใช้ในสตูดิโอของ Android, product_layout_app
แทนที่รหัสmain.dartด้วยรหัสต่อไปนี้ -
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,
เราได้สร้างวิดเจ็ตMyHomePageโดยการขยายStatelessWidgetแทนค่าเริ่มต้นStatefulWidgetจากนั้นจึงลบโค้ดที่เกี่ยวข้องออก
ตอนนี้สร้างวิดเจ็ตใหม่ProductBoxตามการออกแบบที่ระบุดังที่แสดงด้านล่าง -
รหัสสำหรับ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: 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()),
],
)
)
)
]
)
)
);
}
}
โปรดสังเกตสิ่งต่อไปนี้ในรหัส -
ProductBoxได้ใช้สี่อาร์กิวเมนต์ตามที่ระบุด้านล่าง -
ชื่อ - ชื่อผลิตภัณฑ์
description - รายละเอียดสินค้า
price - ราคาของผลิตภัณฑ์
รูปภาพ - รูปภาพของผลิตภัณฑ์
ProductBoxใช้เจ็ดวิดเจ็ตในตัวตามที่ระบุด้านล่าง -
- Container
- Expanded
- Row
- Column
- Card
- Text
- Image
ProductBoxได้รับการออกแบบโดยใช้วิดเจ็ตที่กล่าวถึงข้างต้น การจัดเรียงหรือลำดับชั้นของวิดเจ็ตระบุไว้ในแผนภาพที่แสดงด้านล่าง -
ตอนนี้วางภาพจำลอง (ดูด้านล่าง) สำหรับข้อมูลผลิตภัณฑ์ในโฟลเดอร์ assets ของแอปพลิเคชันและกำหนดค่าโฟลเดอร์ assets ในไฟล์ pubspec.yaml ดังที่แสดงด้านล่าง -
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
สุดท้ายใช้วิดเจ็ตProductBoxในวิดเจ็ตMyHomePageตามที่ระบุด้านล่าง -
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"
),
],
)
);
}
}
ที่นี่เราใช้ProductBoxเป็นลูกของวิดเจ็ตListView
รหัสที่สมบูรณ์(main.dart)ของแอปพลิเคชันเค้าโครงผลิตภัณฑ์(product_layout_app)มีดังนี้ -
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()
),
],
)
)
)
]
)
)
);
}
}
ผลลัพธ์สุดท้ายของแอปพลิเคชันมีดังนี้ -
ท่าทางสัมผัสเป็นวิธีหลักสำหรับผู้ใช้ในการโต้ตอบกับแอปพลิเคชันมือถือ (หรืออุปกรณ์ที่ใช้ระบบสัมผัส) โดยทั่วไปท่าทางสัมผัสหมายถึงการกระทำ / การเคลื่อนไหวใด ๆ ของผู้ใช้เพื่อเปิดใช้งานการควบคุมเฉพาะของอุปกรณ์เคลื่อนที่ ท่าทางสัมผัสทำได้ง่ายเพียงแค่แตะที่หน้าจอของอุปกรณ์เคลื่อนที่ไปจนถึงการกระทำที่ซับซ้อนมากขึ้นที่ใช้ในแอปพลิเคชันเกม
ท่าทางที่ใช้กันอย่างแพร่หลายบางส่วนมีการกล่าวถึงที่นี่ -
Tap - สัมผัสพื้นผิวของอุปกรณ์ด้วยปลายนิ้วเป็นระยะเวลาสั้น ๆ จากนั้นปล่อยปลายนิ้ว
Double Tap - แตะสองครั้งในเวลาสั้น ๆ
Drag - แตะพื้นผิวของอุปกรณ์ด้วยปลายนิ้วจากนั้นขยับปลายนิ้วในลักษณะนิ่ง ๆ จากนั้นจึงปล่อยปลายนิ้วในที่สุด
Flick - คล้ายกับการลาก แต่ทำด้วยวิธีที่เร็วกว่า
Pinch - บีบพื้นผิวของอุปกรณ์โดยใช้สองนิ้ว
Spread/Zoom - ตรงข้ามกับการหนีบ
Panning - สัมผัสพื้นผิวของอุปกรณ์ด้วยปลายนิ้วและเคลื่อนไปในทิศทางใดก็ได้โดยไม่ต้องปล่อยปลายนิ้ว
Flutter ให้การสนับสนุนที่ยอดเยี่ยมสำหรับท่าทางทุกประเภทผ่านวิดเจ็ตพิเศษ GestureDetector. GestureDetector เป็นวิดเจ็ตที่ไม่ใช่ภาพที่ใช้สำหรับตรวจจับท่าทางของผู้ใช้เป็นหลัก ในการระบุท่าทางที่กำหนดเป้าหมายบนวิดเจ็ตวิดเจ็ตสามารถวางไว้ในวิดเจ็ต GestureDetector GestureDetector จะจับท่าทางและส่งเหตุการณ์ต่างๆตามท่าทาง
ท่าทางบางส่วนและเหตุการณ์ที่เกี่ยวข้องมีให้ด้านล่าง -
- Tap
- onTapDown
- onTapUp
- onTap
- onTapCancel
- แตะสองครั้ง
- onDoubleTap
- กดแบบยาว
- onLongPress
- ลากแนวตั้ง
- onVerticalDragStart
- onVerticalDragUpdate
- onVerticalDragEnd
- ลากแนวนอน
- onHorizontalDragStart
- onHorizontalDragUpdate
- onHorizontalDragEnd
- Pan
- onPanStart
- onPanUpdate
- onPanEnd
ตอนนี้ให้เราปรับเปลี่ยนแอปพลิเคชัน hello world เพื่อรวมคุณสมบัติการตรวจจับท่าทางและพยายามทำความเข้าใจแนวคิด
เปลี่ยนเนื้อหาของวิดเจ็ตMyHomePageดังที่แสดงด้านล่าง -
body: Center(
child: GestureDetector(
onTap: () {
_showDialog(context);
},
child: Text( 'Hello World', )
)
),
สังเกตว่าที่นี่เราได้วางวิดเจ็ตGestureDetectorไว้เหนือวิดเจ็ต Text ในลำดับชั้นวิดเจ็ตจับเหตุการณ์ onTap จากนั้นจึงแสดงหน้าต่างโต้ตอบในที่สุด
ใช้ * * * * * * * * _showDialog ฟังก์ชั่นการโต้ตอบในปัจจุบันเมื่อแท็บการใช้ข้อความ Hello World ใช้วิดเจ็ตshowDialogและAlertDialogทั่วไปเพื่อสร้างวิดเจ็ตโต้ตอบใหม่ รหัสดังแสดงด้านล่าง -
// 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();
},
),
],
);
},
);
}
แอปพลิเคชันจะโหลดซ้ำในอุปกรณ์โดยใช้คุณสมบัติ Hot Reload ตอนนี้เพียงคลิกที่ข้อความ Hello World และจะแสดงกล่องโต้ตอบดังต่อไปนี้ -
ตอนนี้ปิดกล่องโต้ตอบโดยคลิกตัวเลือกปิดในกล่องโต้ตอบ
รหัสที่สมบูรณ์ (main.dart) มีดังนี้ -
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', )
)
),
);
}
}
สุดท้าย Flutter ยังมีกลไกการตรวจจับท่าทางระดับต่ำผ่านวิดเจ็ตListener มันจะตรวจจับการโต้ตอบของผู้ใช้ทั้งหมดแล้วยื้อเหตุการณ์ต่อไปนี้ -
- PointerDownEvent
- PointerMoveEvent
- PointerUpEvent
- PointerCancelEvent
Flutter ยังมีวิดเจ็ตชุดเล็ก ๆ สำหรับทำท่าทางเฉพาะและท่าทางขั้นสูง วิดเจ็ตอยู่ด้านล่าง -
Dismissible - รองรับท่าทางการปัดเพื่อปิดวิดเจ็ต
Draggable - รองรับท่าทางลากเพื่อย้ายวิดเจ็ต
LongPressDraggable - รองรับท่าทางลากเพื่อย้ายวิดเจ็ตเมื่อวิดเจ็ตหลักสามารถลากได้
DragTarget- ยอมรับวิดเจ็ตที่ลากได้
IgnorePointer - ซ่อนวิดเจ็ตและลูก ๆ จากกระบวนการตรวจจับท่าทาง
AbsorbPointer - หยุดกระบวนการตรวจจับท่าทางเองดังนั้นวิดเจ็ตใด ๆ ที่ทับซ้อนกันก็ไม่สามารถเข้าร่วมในกระบวนการตรวจจับท่าทางได้ดังนั้นจึงไม่มีเหตุการณ์ใดเกิดขึ้น
Scrollable - รองรับการเลื่อนเนื้อหาที่มีอยู่ภายในวิดเจ็ต
การจัดการสถานะในแอปพลิเคชันเป็นกระบวนการที่สำคัญและจำเป็นที่สุดกระบวนการหนึ่งในวงจรชีวิตของแอปพลิเคชัน
ให้เราพิจารณาแอปพลิเคชันตะกร้าสินค้าง่ายๆ
ผู้ใช้จะเข้าสู่ระบบโดยใช้ข้อมูลประจำตัวในแอปพลิเคชัน
เมื่อผู้ใช้เข้าสู่ระบบแอปพลิเคชันควรคงรายละเอียดผู้ใช้ที่ล็อกอินไว้ในทุกหน้าจอ
อีกครั้งเมื่อผู้ใช้เลือกผลิตภัณฑ์และบันทึกลงในรถเข็นข้อมูลรถเข็นควรคงอยู่ระหว่างหน้าจนกว่าผู้ใช้จะชำระเงินในรถเข็น
ผู้ใช้และข้อมูลรถเข็นของพวกเขาในทุกกรณีเรียกว่าสถานะของแอปพลิเคชันที่อินสแตนซ์นั้น
การจัดการสถานะสามารถแบ่งออกเป็นสองประเภทตามระยะเวลาที่สถานะเฉพาะอยู่ในแอปพลิเคชัน
Ephemeral- คงอยู่ไม่กี่วินาทีเช่นสถานะปัจจุบันของภาพเคลื่อนไหวหรือหน้าเดียวเช่นการให้คะแนนปัจจุบันของผลิตภัณฑ์ Flutterรองรับผ่าน StatefulWidget
app state- สุดท้ายสำหรับแอปพลิเคชันทั้งหมดเช่นรายละเอียดผู้ใช้ที่เข้าสู่ระบบข้อมูลรถเข็น ฯลฯFlutterรองรับผ่าน scoped_model
การนำทางและการกำหนดเส้นทาง
ในแอปพลิเคชันใด ๆ การไปยังอีกหน้าหนึ่งจะกำหนดขั้นตอนการทำงานของแอปพลิเคชัน วิธีจัดการการนำทางของแอปพลิเคชันเรียกว่าการกำหนดเส้นทาง Flutter จัดเตรียมคลาสการกำหนดเส้นทางพื้นฐาน - MaterialPageRoute และสองวิธี - Navigator.push และ Navigator.pop เพื่อกำหนดขั้นตอนการทำงานของแอ็พพลิเคชัน
MaterialPageRoute
MaterialPageRoute เป็นวิดเจ็ตที่ใช้ในการแสดงผล UI โดยแทนที่ทั้งหน้าจอด้วยภาพเคลื่อนไหวเฉพาะแพลตฟอร์ม
MaterialPageRoute(builder: (context) => Widget())
ที่นี่ตัวสร้างจะยอมรับฟังก์ชันในการสร้างเนื้อหาโดยรองรับบริบทปัจจุบันของแอปพลิเคชัน
Navigation.push
Navigation.push ใช้เพื่อนำทางไปยังหน้าจอใหม่โดยใช้วิดเจ็ต MaterialPageRoute
Navigator.push( context, MaterialPageRoute(builder: (context) => Widget()), );
Navigation.pop
Navigation.pop ใช้เพื่อนำทางไปยังหน้าจอก่อนหน้า
Navigator.pop(context);
ให้เราสร้างแอปพลิเคชันใหม่เพื่อให้เข้าใจแนวคิดการนำทางได้ดีขึ้น
สร้างแอปพลิเคชั่น Flutter ใหม่ใน Android studio product_nav_app
คัดลอกโฟลเดอร์ assets จาก product_nav_app ไปยัง product_state_app และเพิ่มเนื้อหาภายในไฟล์ 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
แทนที่รหัสเริ่มต้นเริ่มต้น (main.dart) ด้วยรหัสเริ่มต้นของเรา
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',)
),
);
}
}
ให้เราสร้างคลาสผลิตภัณฑ์เพื่อจัดระเบียบข้อมูลผลิตภัณฑ์
class Product {
final String name;
final String description;
final int price;
final String image;
Product(this.name, this.description, this.price, this.image);
}
ให้เราเขียนเมธอด getProducts ในคลาส Product เพื่อสร้างเร็กคอร์ดผลิตภัณฑ์ดัมมี่ของเรา
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';
ให้เรารวมวิดเจ็ตใหม่ของเรา 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,
),
),
],
);
}
}
ให้เราปรับเปลี่ยนวิดเจ็ต ProductBox เพื่อทำงานกับคลาสผลิตภัณฑ์ใหม่ของเรา
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(),
],
)
)
)
]
),
)
);
}
}
ให้เราเขียนวิดเจ็ต MyHomePage ของเราใหม่เพื่อทำงานกับรุ่นผลิตภัณฑ์และแสดงรายการผลิตภัณฑ์ทั้งหมดโดยใช้ 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]),
),
);
},
);
},
));
}
}
ที่นี่เราได้ใช้ MaterialPageRoute เพื่อไปที่หน้ารายละเอียดผลิตภัณฑ์
ตอนนี้ให้เราเพิ่ม ProductPage เพื่อแสดงรายละเอียดสินค้า
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(),
],
)
)
)
]
),
),
),
);
}
}
รหัสที่สมบูรณ์ของแอปพลิเคชันมีดังนี้ -
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(),
],
)
)
)
]
),
)
);
}
}
เรียกใช้แอปพลิเคชันและคลิกรายการผลิตภัณฑ์ใด ๆ จะแสดงหน้ารายละเอียดที่เกี่ยวข้อง เราสามารถย้ายไปที่โฮมเพจได้โดยคลิกปุ่มย้อนกลับ หน้ารายการสินค้าและหน้ารายละเอียดสินค้าของแอพพลิเคชั่นแสดงดังนี้ -
ภาพเคลื่อนไหวเป็นขั้นตอนที่ซับซ้อนในแอปพลิเคชันมือถือใด ๆ แม้จะมีความซับซ้อน แต่แอนิเมชั่นก็ช่วยเพิ่มประสบการณ์ของผู้ใช้ไปอีกระดับและให้การโต้ตอบกับผู้ใช้ที่หลากหลาย เนื่องจากความมีชีวิตชีวาแอนิเมชั่นจึงกลายเป็นส่วนสำคัญของแอปพลิเคชันมือถือสมัยใหม่ เฟรมเวิร์ก Flutter ตระหนักถึงความสำคัญของแอนิเมชั่นและมอบเฟรมเวิร์กที่เรียบง่ายและใช้งานง่ายเพื่อพัฒนาแอนิเมชั่นทุกประเภท
บทนำ
แอนิเมชั่นคือกระบวนการแสดงชุดภาพ / ภาพตามลำดับเฉพาะภายในช่วงเวลาที่กำหนดเพื่อให้เกิดภาพลวงตาของการเคลื่อนไหว สิ่งที่สำคัญที่สุดของแอนิเมชั่นมีดังนี้ -
ภาพเคลื่อนไหวมีค่าที่แตกต่างกัน 2 ค่า ได้แก่ ค่าเริ่มต้นและค่าสิ้นสุด ภาพเคลื่อนไหวเริ่มต้นจากค่าเริ่มต้นและผ่านชุดของค่ากลางและสุดท้ายจะสิ้นสุดที่ค่าสิ้นสุด ตัวอย่างเช่นหากต้องการทำให้วิดเจ็ตเคลื่อนไหวให้จางหายไปค่าเริ่มต้นจะเป็นค่าความทึบเต็มและค่าสุดท้ายจะเป็นค่าความทึบเป็นศูนย์
ค่ากลางอาจเป็นเชิงเส้นหรือไม่ใช่เชิงเส้น (เส้นโค้ง) และสามารถกำหนดค่าได้ ทำความเข้าใจว่าภาพเคลื่อนไหวทำงานตามที่กำหนดค่าไว้ การกำหนดค่าแต่ละรายการให้ความรู้สึกที่แตกต่างกันกับภาพเคลื่อนไหว ตัวอย่างเช่นการซีดจางวิดเจ็ตจะมีลักษณะเป็นเส้นตรงในขณะที่การกระดอนของลูกบอลจะไม่เป็นเส้นตรง
ระยะเวลาของกระบวนการแอนิเมชั่นมีผลต่อความเร็ว (ความช้าหรือความเร็ว) ของแอนิเมชั่น
ความสามารถในการควบคุมกระบวนการแอนิเมชั่นเช่นการเริ่มแอนิเมชั่นการหยุดแอนิเมชั่นการทำซ้ำแอนิเมชั่นเพื่อกำหนดจำนวนครั้งการย้อนกระบวนการของแอนิเมชั่นเป็นต้น
ใน Flutter ระบบแอนิเมชั่นไม่ได้ทำแอนิเมชั่นจริง แต่จะให้เฉพาะค่าที่จำเป็นในทุกเฟรมเพื่อแสดงภาพ
คลาสแอนิเมชั่น
ระบบภาพเคลื่อนไหวกระพือขึ้นอยู่กับวัตถุแอนิเมชั่น คลาสแอนิเมชั่นหลักและการใช้งานมีดังนี้ -
ภาพเคลื่อนไหว
สร้างค่าที่ถูกแก้ไขระหว่างตัวเลขสองตัวในช่วงเวลาหนึ่ง คลาสแอนิเมชั่นที่พบมากที่สุด ได้แก่ -
Animation<double> - แก้ไขค่าระหว่างทศนิยมสองจำนวน
Animation<Color> - สอดแทรกสีระหว่างสองสี
Animation<Size> - สอดแทรกขนาดระหว่างสองขนาด
AnimationController- วัตถุแอนิเมชันพิเศษเพื่อควบคุมภาพเคลื่อนไหวเอง สร้างค่าใหม่เมื่อใดก็ตามที่แอปพลิเคชันพร้อมสำหรับเฟรมใหม่ รองรับแอนิเมชั่นเชิงเส้นและค่าเริ่มตั้งแต่ 0.0 ถึง 1.0
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
ที่นี่ตัวควบคุมจะควบคุมตัวเลือกภาพเคลื่อนไหวและระยะเวลาควบคุมระยะเวลาของกระบวนการแอนิเมชั่น vsync เป็นตัวเลือกพิเศษที่ใช้เพื่อเพิ่มประสิทธิภาพทรัพยากรที่ใช้ในภาพเคลื่อนไหว
CurvedAnimation
คล้ายกับ AnimationController แต่รองรับภาพเคลื่อนไหวที่ไม่ใช่เชิงเส้น CurvedAnimation สามารถใช้ร่วมกับวัตถุ Animation ได้ดังนี้ -
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)
ทวี <T>
ได้มาจาก Animatable <T> และใช้เพื่อสร้างตัวเลขระหว่างตัวเลขสองตัวใด ๆ ที่ไม่ใช่ 0 และ 1 สามารถใช้ร่วมกับออบเจ็กต์แอนิเมชั่นได้โดยใช้วิธีการเคลื่อนไหวและส่งผ่านวัตถุแอนิเมชั่นจริง
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this); Animation<int> customTween = IntTween(
begin: 0, end: 255).animate(controller);
Tween ยังสามารถใช้ร่วมกับ CurvedAnimation ได้ดังนี้ -
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);
ที่นี่ตัวควบคุมคือตัวควบคุมภาพเคลื่อนไหวที่แท้จริง เส้นโค้งให้ประเภทของความไม่เป็นเชิงเส้นและ customTween ให้ช่วงที่กำหนดเองตั้งแต่ 0 ถึง 255
ขั้นตอนการทำงานของ Flutter Animation
ขั้นตอนการทำงานของภาพเคลื่อนไหวมีดังนี้ -
กำหนดและเริ่มตัวควบคุมแอนิเมชันใน initState ของ StatefulWidget
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
เพิ่มแอนิเมชั่นตามฟัง addListener เพื่อเปลี่ยนสถานะของวิดเจ็ต
animation = Tween<double>(begin: 0, end: 300).animate(controller) ..addListener(() {
setState(() {
// The state that has changed here is the animation object’s value.
});
});
สามารถใช้วิดเจ็ต Build-in, AnimatedWidget และ AnimatedBuilder เพื่อข้ามขั้นตอนนี้ วิดเจ็ตทั้งสองยอมรับออบเจ็กต์ Animation และรับค่าปัจจุบันที่จำเป็นสำหรับภาพเคลื่อนไหว
รับค่าแอนิเมชั่นระหว่างขั้นตอนการสร้างวิดเจ็ตจากนั้นนำไปใช้กับความกว้างความสูงหรือคุณสมบัติที่เกี่ยวข้องแทนค่าเดิม
child: Container(
height: animation.value,
width: animation.value,
child: <Widget>,
)
แอปพลิเคชันการทำงาน
ให้เราเขียนแอปพลิเคชั่นที่ใช้แอนิเมชั่นอย่างง่ายเพื่อทำความเข้าใจแนวคิดของแอนิเมชั่นใน Flutter framework
สร้างแอปพลิเคชันFlutterใหม่ใน Android studio, product_animation_app
คัดลอกโฟลเดอร์ assets จาก product_nav_app ไปยัง product_animation_app และเพิ่มเนื้อหาภายในไฟล์ 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
ลบรหัสเริ่มต้นเริ่มต้น (main.dart)
เพิ่มการนำเข้าและฟังก์ชันหลักพื้นฐาน
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
สร้างวิดเจ็ต MyApp ที่ได้มาจาก StatefulWidgtet
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
สร้างวิดเจ็ต _MyAppState และใช้งาน initState และกำจัดนอกเหนือจากวิธีการสร้างเริ่มต้น
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();
}
}
ที่นี่
ในวิธีการ initState เราได้สร้างออบเจ็กต์ตัวควบคุมแอนิเมชั่น (ตัวควบคุม) ออบเจ็กต์แอนิเมชั่น (แอนิเมชั่น) และเริ่มแอนิเมชั่นโดยใช้ controller.forward
ในวิธีการกำจัดเราได้กำจัดออบเจ็กต์ตัวควบคุมแอนิเมชั่น (คอนโทรลเลอร์)
ในวิธีการสร้างส่งภาพเคลื่อนไหวไปยังวิดเจ็ต MyHomePage ผ่านตัวสร้าง ตอนนี้วิดเจ็ต MyHomePage สามารถใช้ออบเจ็กต์แอนิเมชั่นเพื่อทำให้เนื้อหาเคลื่อนไหวได้
ตอนนี้เพิ่มวิดเจ็ต 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()),
],
)
)
)
]
)
)
);
}
}
สร้างวิดเจ็ตใหม่ MyAnimateWidget เพื่อทำแอนิเมชั่นจาง ๆ ง่ายๆโดยใช้ความทึบ
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),
);
}
ที่นี่เราได้ใช้ AniatedBuilder เพื่อสร้างแอนิเมชั่นของเรา AnimatedBuilder เป็นวิดเจ็ตที่สร้างเนื้อหาในขณะที่ทำแอนิเมชั่นในเวลาเดียวกัน ยอมรับออบเจ็กต์แอนิเมชั่นเพื่อรับค่าแอนิเมชันปัจจุบัน เราได้ใช้ค่าภาพเคลื่อนไหว animation.value เพื่อตั้งค่าความทึบของวิดเจ็ตลูก ผลกระทบวิดเจ็ตจะทำให้วิดเจ็ตลูกเคลื่อนไหวโดยใช้แนวคิดความทึบ
สุดท้ายสร้างวิดเจ็ต MyHomePage และใช้ออบเจ็กต์แอนิเมชั่นเพื่อทำให้เนื้อหาใด ๆ ของมันเคลื่อนไหว
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"
),
],
)
);
}
}
ที่นี่เราได้ใช้ FadeAnimation และ MyAnimationWidget เพื่อทำให้สองรายการแรกในรายการเคลื่อนไหว FadeAnimation เป็นคลาสแอนิเมชั่นในตัวซึ่งเราใช้ในการทำให้เด็กเคลื่อนไหวโดยใช้แนวคิดทึบแสง
รหัสที่สมบูรณ์มีดังนี้ -
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
),
);
}
รวบรวมและเรียกใช้แอปพลิเคชันเพื่อดูผลลัพธ์ เวอร์ชันเริ่มต้นและเวอร์ชันสุดท้ายของแอปพลิเคชันมีดังนี้ -
Flutter จัดเตรียมเฟรมเวิร์กทั่วไปในการเข้าถึงคุณลักษณะเฉพาะของแพลตฟอร์ม สิ่งนี้ช่วยให้นักพัฒนาสามารถขยายการทำงานของเฟรมเวิร์กFlutterโดยใช้รหัสเฉพาะแพลตฟอร์ม ฟังก์ชันเฉพาะของแพลตฟอร์มเช่นกล้องระดับแบตเตอรี่เบราว์เซอร์ ฯลฯ สามารถเข้าถึงได้อย่างง่ายดายผ่านเฟรมเวิร์ก
แนวคิดทั่วไปในการเข้าถึงรหัสเฉพาะแพลตฟอร์มคือผ่านโปรโตคอลการส่งข้อความธรรมดา Flutter code ไคลเอนต์และรหัสแพลตฟอร์มและโฮสต์เชื่อมโยงกับช่องข้อความทั่วไป ลูกค้าส่งข้อความไปยังโฮสต์ผ่านช่องข้อความ โฮสต์รับฟังข้อความในช่องข้อความรับข้อความและทำหน้าที่ที่จำเป็นและสุดท้ายส่งผลลัพธ์กลับไปยังลูกค้าผ่านช่องข้อความ
สถาปัตยกรรมโค้ดเฉพาะแพลตฟอร์มแสดงในแผนภาพบล็อกที่ระบุด้านล่าง -
โปรโตคอลการส่งข้อความใช้ตัวแปลงรหัสข้อความมาตรฐาน (คลาส StandardMessageCodec) ที่สนับสนุนการจัดลำดับไบนารีของค่าที่คล้าย JSON เช่นตัวเลขสตริงบูลีนเป็นต้นการทำให้เป็นอนุกรมและการทำให้เป็นอนุกรมทำงานอย่างโปร่งใสระหว่างไคลเอนต์และโฮสต์
ให้เราเขียนแอปพลิเคชันง่ายๆเพื่อเปิดเบราว์เซอร์โดยใช้Android SDKและทำความเข้าใจวิธีการ
สร้างแอปพลิเคชัน Flutter ใหม่ใน Android studio flutter_browser_app
แทนที่รหัส main.dart ด้วยรหัสด้านล่าง -
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,
),
),
);
}
}
ที่นี่เราได้สร้างปุ่มใหม่เพื่อเปิดเบราว์เซอร์และตั้งค่าเมธอด onPressed เป็น null
ตอนนี้นำเข้าแพ็คเกจต่อไปนี้ -
import 'dart:async';
import 'package:flutter/services.dart';
ที่นี่ services.dart มีฟังก์ชันในการเรียกใช้รหัสเฉพาะแพลตฟอร์ม
สร้างช่องข้อความใหม่ในวิดเจ็ต MyHomePage
static const platform = const
MethodChannel('flutterapp.tutorialspoint.com/browser');
เขียนเมธอด _openBrowser เพื่อเรียกใช้เมธอดเฉพาะแพลตฟอร์มเมธอด openBrowser ผ่านช่องข้อความ
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);
}
}
ที่นี่เราได้ใช้ platform.invokeMethod เพื่อเรียกใช้ openBrowser (อธิบายในขั้นตอนต่อไป) openBrowser มีอาร์กิวเมนต์ url เพื่อเปิด url เฉพาะ
เปลี่ยนค่าของคุณสมบัติ onPressed ของ RaisedButton จาก null เป็น _openBrowser
onPressed: _openBrowser,
เปิด MainActivity.java (ภายในโฟลเดอร์ android) และนำเข้าไลบรารีที่ต้องการ -
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;
เขียนเมธอด openBrowser เพื่อเปิดเบราว์เซอร์
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);
}
ตอนนี้ตั้งชื่อช่องในคลาส MainActivity -
private static final String CHANNEL = "flutterapp.tutorialspoint.com/browser";
เขียนรหัสเฉพาะของ Android เพื่อตั้งค่าการจัดการข้อความในวิธี 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();
}
}
});
ที่นี่เราได้สร้างช่องข้อความโดยใช้คลาส MethodChannel และใช้คลาส MethodCallHandler เพื่อจัดการกับข้อความ onMethodCall เป็นวิธีการจริงที่รับผิดชอบในการเรียกรหัสเฉพาะแพลตฟอร์มที่ถูกต้องโดยการตรวจสอบข้อความ เมธอด onMethodCall ดึง url ออกจากข้อความจากนั้นเรียกใช้ openBrowser เฉพาะเมื่อการเรียกเมธอดเป็น openBrowser มิฉะนั้นจะส่งกลับเมธอด notImplemented
ซอร์สโค้ดที่สมบูรณ์ของแอปพลิเคชันมีดังนี้ -
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,
),
),
);
}
}
เรียกใช้แอปพลิเคชันและคลิกปุ่มเปิดเบราว์เซอร์และคุณจะเห็นว่าเบราว์เซอร์เปิดขึ้น แอพเบราว์เซอร์ - โฮมเพจดังที่แสดงในภาพหน้าจอที่นี่ -
การเข้าถึงรหัสเฉพาะของ iOS จะคล้ายกับบนแพลตฟอร์ม Android ยกเว้นว่าจะใช้ภาษาเฉพาะของ iOS - Objective-C หรือ Swift และ iOS SDK มิฉะนั้นแนวคิดจะเหมือนกับของแพลตฟอร์ม Android
ให้เราเขียนแอปพลิเคชันเดียวกันกับในบทก่อนหน้าสำหรับแพลตฟอร์ม iOS ด้วย
ให้เราสร้างแอปพลิเคชั่นใหม่ใน Android Studio (macOS), flutter_browser_ios_app
ทำตามขั้นตอนที่ 2 - 6 ตามบทก่อนหน้า
เริ่ม XCode แล้วคลิก File → Open
เลือกโครงการ xcode ภายใต้ไดเร็กทอรี ios ของโครงการ flutter ของเรา
เปิด AppDelegate.m ภายใต้ Runner → Runner path. ประกอบด้วยรหัสต่อไปนี้ -
#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
เราได้เพิ่มวิธีการ openBrowser เพื่อเปิดเบราว์เซอร์ด้วย url ที่ระบุ ยอมรับอาร์กิวเมนต์เดียว url
- (void)openBrowser:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
UIApplication *application = [UIApplication sharedApplication];
[application openURL:url];
}
ในเมธอด didFinishLaunchingWithOptions ให้ค้นหาคอนโทรลเลอร์และตั้งค่าในตัวแปรคอนโทรลเลอร์
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
ในวิธี didFinishLaunchingWithOptions ตั้งค่าช่องเบราว์เซอร์เป็น flutterapp.tutorialspoint.com/browse -
FlutterMethodChannel* browserChannel = [
FlutterMethodChannel methodChannelWithName:
@"flutterapp.tutorialspoint.com/browser" binaryMessenger:controller];
สร้างตัวแปรจุดอ่อนตัวเองและตั้งค่าคลาสปัจจุบัน -
__weak typeof(self) weakSelf = self;
ตอนนี้ใช้ setMethodCallHandler เรียก openBrowser โดยจับคู่ call.method รับ url โดยเรียกใช้ call.arguments และส่งผ่านขณะเรียก openBrowser
[browserChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"openBrowser" isEqualToString:call.method]) {
NSString *url = call.arguments[@"url"];
[weakSelf openBrowser:url];
} else { result(FlutterMethodNotImplemented); }
}];
รหัสที่สมบูรณ์มีดังนี้ -
#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
เปิดการตั้งค่าโครงการ
ไปที่ Capabilities และเปิดใช้งาน Background Modes.
เพิ่ม *Background fetch และ Remote Notification**.
ตอนนี้เรียกใช้แอปพลิเคชัน ใช้งานได้คล้ายกับเวอร์ชัน Android แต่จะเปิดเบราว์เซอร์ Safari แทน Chrome
วิธีจัดระเบียบและแบ่งปันชุดฟังก์ชันการทำงานของ Dart คือผ่าน Package Dart Package เป็นไลบรารีหรือโมดูลที่แชร์ได้ โดยทั่วไปแพ็คเกจ Dart จะเหมือนกับแอปพลิเคชัน Dart ยกเว้น Dart Package ไม่มีจุดเข้าแอปพลิเคชันหลัก
โครงสร้างทั่วไปของ Package (พิจารณาแพ็คเกจสาธิต my_demo_package) มีดังต่อไปนี้ -
lib/src/* - ไฟล์รหัส Dart ส่วนตัว
lib/my_demo_package.dart- ไฟล์รหัสโผหลัก สามารถนำเข้าสู่แอปพลิเคชันเป็น -
import 'package:my_demo_package/my_demo_package.dart'
ไฟล์รหัสส่วนตัวอื่น ๆ อาจถูกส่งออกไปยังไฟล์รหัสหลัก (my_demo_package.dart) หากจำเป็นดังที่แสดงด้านล่าง -
export src/my_private_code.dart
lib/*- ไฟล์รหัส Dart จำนวนเท่าใดก็ได้ที่จัดเรียงในโครงสร้างโฟลเดอร์ที่กำหนดเอง รหัสสามารถเข้าถึงได้เป็น
import 'package:my_demo_package/custom_folder/custom_file.dart'
pubspec.yaml - ข้อกำหนดโครงการเช่นเดียวกับการใช้งาน
ไฟล์รหัส Dart ทั้งหมดในแพ็คเกจเป็นเพียงคลาส Dart และไม่มีข้อกำหนดพิเศษใด ๆ สำหรับรหัส Dart ที่จะรวมไว้ในแพ็คเกจ
ประเภทของแพ็คเกจ
เนื่องจาก Dart Packages นั้นมีฟังก์ชันการทำงานที่คล้ายคลึงกันจำนวนเล็กน้อยจึงสามารถแบ่งประเภทตามฟังก์ชันการทำงานได้
โผแพ็คเกจ
รหัส Dart ทั่วไปซึ่งสามารถใช้ได้ทั้งในสภาพแวดล้อมเว็บและอุปกรณ์เคลื่อนที่ ตัวอย่างเช่น english_words เป็นแพ็คเกจหนึ่งที่มีคำประมาณ 5,000 คำและมีฟังก์ชันอรรถประโยชน์พื้นฐานเช่นคำนาม (รายการนามในภาษาอังกฤษ) พยางค์ (ระบุจำนวนพยางค์ในคำ
แพ็คเกจ Flutter
รหัส Dart ทั่วไปซึ่งขึ้นอยู่กับเฟรมเวิร์ก Flutter และสามารถใช้ได้ในสภาพแวดล้อมมือถือเท่านั้น ตัวอย่างเช่น fluro เป็นเราเตอร์ที่กำหนดเองสำหรับการกระพือ ขึ้นอยู่กับเฟรมเวิร์กของ Flutter
ปลั๊กอิน Flutter
รหัส Dart ทั่วไปซึ่งขึ้นอยู่กับเฟรมเวิร์กของ Flutter รวมถึงรหัสแพลตฟอร์มพื้นฐาน (Android SDK หรือ iOS SDK) ตัวอย่างเช่นกล้องเป็นปลั๊กอินสำหรับโต้ตอบกับกล้องของอุปกรณ์ ขึ้นอยู่กับเฟรมเวิร์ก Flutter รวมถึงเฟรมเวิร์กพื้นฐานในการเข้าถึงกล้อง
การใช้ Dart Package
Dart Packages โฮสต์และเผยแพร่ไปยังเซิร์ฟเวอร์สด https://pub.dartlang.org.นอกจากนี้ Flutter ยังมีเครื่องมือง่ายๆผับในการจัดการ Dart Packages ในแอปพลิเคชัน ขั้นตอนที่จำเป็นในการใช้งาน Package มีดังนี้ -
รวมชื่อแพ็กเกจและเวอร์ชันที่ต้องการลงใน pubspec.yaml ดังที่แสดงด้านล่าง -
dependencies: english_words: ^3.1.5
คุณสามารถดูหมายเลขเวอร์ชันล่าสุดได้จากการตรวจสอบเซิร์ฟเวอร์ออนไลน์
ติดตั้งแพ็กเกจลงในแอปพลิเคชันโดยใช้คำสั่งต่อไปนี้ -
flutter packages get
ในขณะที่พัฒนาในสตูดิโอ Android Android Studio จะตรวจพบการเปลี่ยนแปลงใด ๆ ใน pubspec.yaml และแสดงการแจ้งเตือนแพ็คเกจ Android studio ให้กับผู้พัฒนาดังที่แสดงด้านล่าง
สามารถติดตั้งหรืออัปเดต Dart Packages ใน Android Studio ได้โดยใช้ตัวเลือกเมนู
นำเข้าไฟล์ที่จำเป็นโดยใช้คำสั่งที่แสดงด้านล่างและเริ่มทำงาน -
import 'package:english_words/english_words.dart';
ใช้วิธีใดก็ได้ที่มีอยู่ในแพ็คเกจ
nouns.take(50).forEach(print);
ที่นี่เราได้ใช้ฟังก์ชันคำนามเพื่อรับและพิมพ์คำ 50 อันดับแรก
พัฒนาแพ็คเกจปลั๊กอิน Flutter
การพัฒนาปลั๊กอิน Flutter นั้นคล้ายกับการพัฒนาแอปพลิเคชัน Dart หรือ Dart Package ข้อยกเว้นเพียงอย่างเดียวคือปลั๊กอินจะใช้ System API (Android หรือ iOS) เพื่อรับฟังก์ชันเฉพาะแพลตฟอร์มที่จำเป็น
ตามที่เราได้เรียนรู้วิธีการเข้าถึงรหัสแพลตฟอร์มในบทก่อนหน้านี้แล้วให้เราพัฒนาปลั๊กอินง่ายๆ my_browser เพื่อทำความเข้าใจกระบวนการพัฒนาปลั๊กอิน การทำงานของปลั๊กอิน my_browser คือการอนุญาตให้แอปพลิเคชันเปิดเว็บไซต์ที่กำหนดในเบราว์เซอร์เฉพาะของแพลตฟอร์ม
เริ่ม Android Studio
คลิก File → New Flutter Project และเลือกตัวเลือก Flutter Plugin
คุณสามารถเห็นหน้าต่างการเลือกปลั๊กอิน Flutter ดังที่แสดงไว้ที่นี่ -
ป้อน my_browser เป็นชื่อโปรเจ็กต์แล้วคลิกถัดไป
ป้อนชื่อปลั๊กอินและรายละเอียดอื่น ๆ ในหน้าต่างดังแสดงที่นี่ -
ป้อนโดเมน บริษัท flutterplugins.tutorialspoint.com ในหน้าต่างที่แสดงด้านล่างจากนั้นคลิกที่ Finish. มันจะสร้างรหัสเริ่มต้นเพื่อพัฒนาปลั๊กอินใหม่ของเรา
เปิดไฟล์ my_browser.dart และเขียนเมธอด openBrowser เพื่อเรียกใช้เมธอด openBrowser เฉพาะแพลตฟอร์ม
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 และนำเข้าคลาสต่อไปนี้ -
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
ที่นี่เราต้องนำเข้าไลบรารีที่จำเป็นสำหรับการเปิดเบราว์เซอร์จาก Android
เพิ่มตัวแปรส่วนตัวใหม่ mRegistrar ประเภท Registrar ในคลาส MyBrowserPlugin
private final Registrar mRegistrar;
ที่นี่นายทะเบียนใช้เพื่อรับข้อมูลบริบทของรหัสเรียกใช้
เพิ่มตัวสร้างเพื่อตั้งค่า Registrar ในคลาส MyBrowserPlugin
private MyBrowserPlugin(Registrar registrar) {
this.mRegistrar = registrar;
}
เปลี่ยน registerWith รวมตัวสร้างใหม่ของเราในคลาส MyBrowserPlugin
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_browser");
MyBrowserPlugin instance = new MyBrowserPlugin(registrar);
channel.setMethodCallHandler(instance);
}
เปลี่ยน onMethodCall เพื่อรวมเมธอด openBrowser ในคลาส 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();
}
}
เขียนเมธอด openBrowser เฉพาะแพลตฟอร์มเพื่อเข้าถึงเบราว์เซอร์ในคลาส 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);
}
ซอร์สโค้ดที่สมบูรณ์ของปลั๊กอิน my_browser มีดังนี้ -
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);
}
}
สร้างโปรเจ็กต์ใหม่my_browser_plugin_testเพื่อทดสอบปลั๊กอินที่สร้างขึ้นใหม่ของเรา
เปิด pubspec.yaml และตั้งค่า my_browser เป็นการพึ่งพาปลั๊กอิน
dependencies:
flutter:
sdk: flutter
my_browser:
path: ../my_browser
Android studio จะแจ้งเตือนว่า pubspec.yaml ได้รับการอัปเดตตามที่แสดงในการแจ้งเตือนแพ็คเกจ Android studio ที่ระบุด้านล่าง -
คลิกตัวเลือกรับการอ้างอิง Android studio จะได้รับแพ็คเกจจากอินเทอร์เน็ตและกำหนดค่าให้เหมาะสมกับแอปพลิเคชัน
เปิด main.dart และรวมปลั๊กอิน my_browser ดังต่อไปนี้ -
import 'package:my_browser/my_browser.dart';
เรียกใช้ฟังก์ชัน openBrowser จาก my_browser plugin ดังที่แสดงด้านล่าง -
onPressed: () => MyBrowser().openBrowser("https://flutter.dev"),
รหัสที่สมบูรณ์ของ main.dart มีดังนี้ -
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"),
),
),
);
}
}
เรียกใช้แอปพลิเคชันและคลิกปุ่มเปิดเบราว์เซอร์และดูว่าเบราว์เซอร์เปิดขึ้น คุณสามารถดูแอปเบราว์เซอร์ - โฮมเพจดังที่แสดงในภาพหน้าจอด้านล่าง -
คุณสามารถเห็นแอปเบราว์เซอร์ - หน้าจอเบราว์เซอร์ดังที่แสดงในภาพหน้าจอด้านล่าง -
- เพิ่มหมายเหตุ
- คั่นหน้านี้
- รายงานข้อผิดพลาด
- Suggestions
บันทึก ปิด