Flutter ได้พลิกโฉมภูมิทัศน์การพัฒนาอุปกรณ์เคลื่อนที่โดยช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชันที่สวยงามและคอมไพล์แล้วสำหรับอุปกรณ์เคลื่อนที่ เว็บ และเดสก์ท็อปจากโค้ดเบสเดียว ด้วยไลบรารีวิดเจ็ตที่หลากหลาย เฟรมเวิร์ก UI ที่ชัดเจน และฟีเจอร์การโหลดซ้ำยอดนิยม Flutter ช่วยให้ทีมสามารถทำซ้ำได้อย่างรวดเร็วและส่งมอบอินเทอร์เฟซผู้ใช้ที่สวยงามในเวลาที่บันทึก
ในคู่มือที่ครอบคลุมนี้ เราจะสำรวจแนวคิดหลักที่ทำให้ Flutter เป็นตัวเลือกที่ยอดเยี่ยมสำหรับการพัฒนา UI บนมือถืออย่างรวดเร็ว ตั้งแต่การทำความเข้าใจแผนผังวิดเจ็ตไปจนถึงการนำภาพเคลื่อนไหวที่ซับซ้อนและเค้าโครงที่ตอบสนองไปใช้
การทำความเข้าใจแผนผังวิดเจ็ต Flutter
ทุกอย่างใน Flutter นั้นเป็นวิดเจ็ต แอปพลิเคชันทั้งหมดถูกสร้างขึ้นเป็นแผนผังวิดเจ็ต โดยแต่ละวิดเจ็ตจะอธิบายส่วนหนึ่งของอินเทอร์เฟซผู้ใช้ วิธีการประกาศนี้หมายความว่าคุณอธิบายว่า UI ควรมีลักษณะอย่างไรสำหรับสถานะที่กำหนด และ Flutter จะดูแลการอัปเดตการเรนเดอร์เมื่อสถานะเปลี่ยนแปลง
แผนผังวิดเจ็ตเป็นแบบลำดับชั้น ที่ด้านบนจะมีMaterialAppหรือCupertinoAppซึ่งมีธีมและโครงการนำทาง ด้านล่างนี้ คุณจะเขียน UI ของคุณจากวิดเจ็ตขนาดเล็กที่สามารถนำมาใช้ซ้ำได้ การทำความเข้าใจลำดับชั้นนี้เป็นพื้นฐานในการสร้างแอปพลิเคชัน Flutter ที่มีประสิทธิภาพ
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Flutter App',
theme: ThemeData(
colorSchemeSeed: Colors.blue,
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}แต่ละวิดเจ็ตในแผนผังมีElementที่สอดคล้องกันซึ่งแสดงถึงตำแหน่งในแผนผังและRenderObjectที่จัดการเค้าโครงและการลงสี ไปป์ไลน์การเรนเดอร์ของ Flutter กระจายแผนผังวิดเจ็ตอย่างมีประสิทธิภาพเพื่อระบุการอัปเดตขั้นต่ำ ทำให้ UI ของคุณมีประสิทธิภาพแม้ว่าจะสร้างใหม่บ่อยครั้งก็ตาม
Stateless และ Stateful Widgets
Flutter มีวิดเจ็ตพื้นฐานสองประเภทที่ให้บริการตามวัตถุประสงค์ที่แตกต่างกันในสถาปัตยกรรมแอปพลิเคชันของคุณ
StatelessWidget
AStatelessWidgetจะไม่เปลี่ยนรูป เมื่อสร้างขึ้นแล้ว จะไม่สามารถเปลี่ยนสถานะภายในได้ วิดเจ็ตเหล่านี้เหมาะอย่างยิ่งสำหรับองค์ประกอบ UI ที่ขึ้นอยู่กับการกำหนดค่าที่ส่งถึงวิดเจ็ตเหล่านี้และวิดเจ็ตที่สืบทอดที่อยู่ด้านบนในแผนผังเท่านั้น พวกมันมีน้ำหนักเบา ทดสอบได้ง่าย และควรเป็นตัวเลือกเริ่มต้นของคุณ
class UserCard extends StatelessWidget {
final String name;
final String email;
final String avatarUrl;
const UserCard({
super.key,
required this.name,
required this.email,
required this.avatarUrl,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(avatarUrl),
),
title: Text(name),
subtitle: Text(email),
trailing: const Icon(Icons.arrow_forward_ios),
),
);
}
}StatefulWidget
StatefulWidgetรักษาสถานะที่ไม่แน่นอนซึ่งสามารถเปลี่ยนแปลงได้ตลอดอายุการใช้งานของวิดเจ็ต เมื่อสถานะเปลี่ยนแปลงผ่านsetState()Flutter จะสร้างวิดเจ็ตใหม่เพื่อแสดงสถานะใหม่ ใช้วิดเจ็ตเก็บสถานะสำหรับองค์ประกอบเชิงโต้ตอบ เช่น แบบฟอร์ม การสลับ และส่วนประกอบแบบเคลื่อนไหว
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $_count', style: Theme.of(context).textTheme.headlineMedium),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}ระบบเค้าโครง: แถว คอลัมน์ สแต็ก และ Flex ระบบเค้าโครงของ
Flutter สร้างขึ้นจากชุดวิดเจ็ตแบบยืดหยุ่นอันทรงพลัง ซึ่งช่วยให้คุณควบคุมวิธีจัดเรียงองค์ประกอบบนหน้าจอได้อย่างแม่นยำ
แถวและคอลัมน์
Rowจัดเรียงรายการย่อยในแนวนอน ในขณะที่Columnจัดเรียงรายการในแนวตั้ง ทั้งสองรองรับการจัดตำแหน่งตามแกนหลักและแกนขวาง และสามารถกระจายพื้นที่ระหว่างเด็กๆ ได้โดยใช้วิดเจ็ตExpandedและFlexible
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 2,
child: Text('Title', style: Theme.of(context).textTheme.titleLarge),
),
Expanded(
flex: 1,
child: ElevatedButton(
onPressed: () {},
child: const Text('Action'),
),
),
],
)Stack
Stackช่วยให้คุณสามารถวางวิดเจ็ตซ้อนทับกัน นี่เป็นสิ่งล้ำค่าสำหรับการสร้างองค์ประกอบภาพที่ซับซ้อน เช่น ภาพซ้อนทับ ป้าย และองค์ประกอบการเคลื่อนไหวแบบลอย ใช้Positionedเพื่อวางลูกในตำแหน่งเฉพาะภายในสแต็ก
Stack(
children: [
Image.asset('assets/background.jpg', fit: BoxFit.cover),
Positioned(
bottom: 16,
left: 16,
right: 16,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.black54,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
'Overlay Text',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
),
],
)รูปแบบเลย์เอาต์ที่ยืดหยุ่น
รวมExpanded,FlexibleและSpacerเพื่อสร้างเลย์เอาต์ที่ซับซ้อนที่ปรับให้เข้ากับพื้นที่ว่างExpandedบังคับให้เด็กเติมพื้นที่ที่เหลือ ในขณะที่Flexibleอนุญาตให้เด็กมีขนาดเล็กกว่าแฟกเตอร์ Flex ที่จัดสรรไว้
การออกแบบที่ตอบสนองด้วย MediaQuery และ LayoutBuilder
การสร้าง UI แบบตอบสนองที่ทำงานบนโทรศัพท์ แท็บเล็ต และแม้แต่เดสก์ท็อป จำเป็นต้องมีความเข้าใจเครื่องมือออกแบบที่ตอบสนองของ Flutter
MediaQueryให้ข้อมูลเกี่ยวกับอุปกรณ์ปัจจุบัน รวมถึงขนาดหน้าจอ การวางแนว ความหนาแน่นของพิกเซล และการตั้งค่าระบบ ใช้ตัดสินใจเค้าโครงตามพื้นที่ว่าง
class ResponsiveLayout extends StatelessWidget {
const ResponsiveLayout({super.key});
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final isTablet = screenWidth > 600;
final isDesktop = screenWidth > 1200;
return Scaffold(
body: isDesktop
? Row(
children: [
const NavigationRail(destinations: []),
Expanded(child: _buildContent(crossAxisCount: 4)),
],
)
: _buildContent(crossAxisCount: isTablet ? 3 : 2),
);
}
Widget _buildContent({required int crossAxisCount}) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: 20,
itemBuilder: (context, index) => Card(
child: Center(child: Text('Item $index')),
),
);
}
}LayoutBuilderเป็นอีกหนึ่งเครื่องมืออันทรงพลังที่ให้ข้อจำกัดของวิดเจ็ตพาเรนต์ ช่วยให้คุณสร้างเลย์เอาต์ที่แตกต่างกันตามพื้นที่ที่มีอยู่จริง แทนที่จะเป็นขนาดเต็มหน้าจอ สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับส่วนประกอบที่นำกลับมาใช้ใหม่ได้ซึ่งจำเป็นต้องปรับให้เข้ากับคอนเทนเนอร์
ธีมและการออกแบบวัสดุ 3
Flutter ระบบธีมของ Flutter ช่วยให้คุณกำหนดภาษาภาพที่สอดคล้องกันทั่วทั้งแอปพลิเคชันของคุณ ด้วย Material Design 3 คุณสามารถสร้างชุดสีที่สมบูรณ์จากสีเมล็ดเดียวได้
MaterialApp(
theme: ThemeData(
colorSchemeSeed: const Color(0xFF1A73E8),
useMaterial3: true,
textTheme: const TextTheme(
headlineLarge: TextStyle(fontWeight: FontWeight.bold),
bodyLarge: TextStyle(fontSize: 16, height: 1.5),
),
cardTheme: CardTheme(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(color: Colors.grey.shade200),
),
),
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
filled: true,
),
),
)เข้าถึงคุณสมบัติธีมได้ทุกที่ในแผนผังวิดเจ็ตของคุณโดยใช้Theme.of(context)สิ่งนี้ทำให้มั่นใจได้ถึงความสอดคล้องของภาพ และทำให้การใช้โหมดมืดโดยการจัดเตรียมdarkThemeสำรองให้กับMaterialAppของคุณเป็นเรื่องเล็กน้อย
แอนิเมชั่นและการเปลี่ยนภาพ
Flutter เป็นเลิศในด้านแอนิเมชั่นที่ราบรื่นและมีประสิทธิภาพ เฟรมเวิร์กจัดเตรียม API แอนิเมชันทั้งโดยนัยและชัดเจนเพื่อให้เหมาะกับระดับความซับซ้อนที่แตกต่างกัน
ภาพเคลื่อนไหวโดยนัย
สำหรับการเปลี่ยนแปลงคุณสมบัติอย่างง่าย ภาพเคลื่อนไหวโดยนัยจะเคลื่อนไหวโดยอัตโนมัติระหว่างค่าเก่าและค่าใหม่ วิดเจ็ตเช่นAnimatedContainer,AnimatedOpacityและAnimatedPaddingต้องใช้โค้ดเพียงเล็กน้อย
AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: _isExpanded ? 300 : 150,
height: _isExpanded ? 200 : 100,
decoration: BoxDecoration(
color: _isExpanded ? Colors.blue : Colors.green,
borderRadius: BorderRadius.circular(_isExpanded ? 16 : 8),
),
child: const Center(child: Text('Tap me')),
)ภาพเคลื่อนไหวที่ชัดเจน
เพื่อการควบคุมที่มากขึ้น ให้ใช้AnimationControllerกับวัตถุTweenสิ่งนี้ช่วยให้คุณควบคุมช่วงเวลา ลำดับ และเส้นโค้งแอนิเมชั่นได้อย่างละเอียด
class PulseAnimation extends StatefulWidget {
const PulseAnimation({super.key});
@override
State<PulseAnimation> createState() => _PulseAnimationState();
}
class _PulseAnimationState extends State<PulseAnimation>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
)..repeat(reverse: true);
_scaleAnimation = Tween<double>(begin: 0.95, end: 1.05).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: _scaleAnimation,
child: const FlutterLogo(size: 100),
);
}
}การเปลี่ยนหน้า
ปรับแต่งการเปลี่ยนการนำทางโดยใช้PageRouteBuilderเพื่อสร้างการเปลี่ยนหน้าจอที่โดดเด่นซึ่งช่วยเสริมบุคลิกของแอปของคุณ
ขั้นตอนการโหลดซ้ำด่วน
การโหลดซ้ำด่วนของ Flutter เป็นตัวเปลี่ยนเกมสำหรับความเร็วในการพัฒนา เมื่อคุณบันทึกไฟล์ Flutter จะแทรกซอร์สโค้ดที่อัปเดตลงใน Dart VM ที่กำลังทำงานอยู่ โดยคงสถานะปัจจุบันของแอปของคุณ ซึ่งหมายความว่าคุณสามารถปรับแต่งช่องว่างภายในของวิดเจ็ต เปลี่ยนสี หรือปรับโครงสร้างเค้าโครง และดูผลลัพธ์ได้ภายในไม่กี่วินาที
เพื่อเพิ่มประสิทธิภาพการโหลดซ้ำร้อน ให้ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้:
- เก็บวิธีการสร้างบริสุทธิ์ - วิธีการสร้างควรทำเท่านั้น ขึ้นอยู่กับสถานะของวิดเจ็ตและวิดเจ็ตที่สืบทอดมา ผลข้างเคียงในวิธีการ build อาจทำให้เกิดการทำงานที่ไม่คาดคิดในระหว่างการรีโหลดแบบ hot
- ใช้ตัวสร้าง const- ทำเครื่องหมายวิดเจ็ตเป็น
constเมื่อเป็นไปได้ ซึ่งจะช่วยให้ Flutter ระบุได้ว่าส่วนใดของแผนผังจำเป็นต้องสร้างใหม่และปรับปรุงประสิทธิภาพ - แยกวิดเจ็ตออกเป็นคลาสที่แยกจากกัน- แทนที่จะฝังลึก ให้แยกส่วน UI แบบลอจิคัลลงในคลาสวิดเจ็ตของตัวเอง สิ่งนี้จะช่วยปรับปรุงรายละเอียดการรีโหลดแบบ hot reload และความสามารถในการอ่านโค้ด
- ใช้ประโยชน์จาก DevTools- Flutter DevTools มอบเครื่องมือตรวจสอบวิดเจ็ต เครื่องมือสร้างโปรไฟล์ประสิทธิภาพ และเครื่องมือสำรวจเลย์เอาต์ที่จับคู่อย่างสมบูรณ์แบบกับการรีโหลดซ้ำอย่างรวดเร็วเพื่อการวนซ้ำที่รวดเร็ว
เคล็ดลับประสิทธิภาพสำหรับ Flutter UI
การสร้าง UI ที่รวดเร็วจำเป็นต้องให้ความสนใจในหลายประเด็นสำคัญ:
1. ใช้ const Widgets
การประกาศวิดเจ็ต เนื่องจากconstอนุญาตให้ Flutter แคชวิดเจ็ตเหล่านั้นและข้ามการสร้างใหม่ทั้งหมด นี่เป็นหนึ่งในการเพิ่มประสิทธิภาพที่ง่ายที่สุดและมีประสิทธิภาพมากที่สุด
2. ย่อขอบเขตการสร้างใหม่ให้เล็กสุด
เมื่อใช้setState()เฉพาะวิดเจ็ตที่เรียกและรายการสืบทอดที่สร้างขึ้นใหม่ รักษาวิดเจ็ต stateful ของคุณให้เล็กและเน้นเพื่อจำกัดรัศมีการระเบิดของการเปลี่ยนแปลงสถานะ
3. ใช้ ListView.builder สำหรับรายการแบบยาว
ห้ามใช้ListViewกับรายการลูกแบบคงที่สำหรับชุดข้อมูลขนาดใหญ่ ให้ใช้ListView.builderซึ่งสร้างรายการอย่างเกียจคร้านในขณะที่เลื่อนดู ช่วยลดการใช้หน่วยความจำได้อย่างมาก
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index].title),
subtitle: Text(items[index].description),
);
},
)4. การคำนวณแคชที่มีราคาแพง
ใช้วิดเจ็ตRepaintBoundaryเพื่อแยกส่วนที่เปลี่ยนแปลงบ่อยของ UI ออกจากส่วนที่คงที่ ซึ่งจะช่วยป้องกันการทาสีเนื้อหาที่เสถียรซ้ำโดยไม่จำเป็น
5. ปรับภาพให้เหมาะสม
ใช้ภาพที่มีขนาดเหมาะสม และพิจารณาใช้cached_network_imageสำหรับภาพเครือข่าย ระบุcacheWidthและcacheHeightบนวิดเจ็ตImageเพื่อถอดรหัสรูปภาพในขนาดที่แสดงแทนที่จะเป็นความละเอียดเต็ม
การสร้าง UI ที่สมบูรณ์ ตัวอย่าง
ให้เรานำทุกอย่างมารวมกันด้วยตัวอย่างเชิงปฏิบัติที่ผสมผสานเค้าโครง ธีม และการโต้ตอบเข้าด้วยกัน
class ProductCard extends StatelessWidget {
final String title;
final String price;
final String imageUrl;
final VoidCallback onAddToCart;
const ProductCard({
super.key,
required this.title,
required this.price,
required this.imageUrl,
required this.onAddToCart,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: Image.network(imageUrl, fit: BoxFit.cover),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
Text(price, style: theme.textTheme.titleLarge?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.bold,
)),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: FilledButton.icon(
onPressed: onAddToCart,
icon: const Icon(Icons.shopping_cart),
label: const Text('Add to Cart'),
),
),
],
),
),
],
),
);
}
}สรุป
Flutter มอบสภาพแวดล้อมที่มีประสิทธิผลอย่างเหลือเชื่อสำหรับการสร้าง UI บนมือถือ สถาปัตยกรรมแบบวิดเจ็ต ผสมผสานกับการรีโหลดแบบด่วน ระบบธีมที่หลากหลาย และเค้าโครงดั้งเดิมอันทรงพลัง ช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชั่นที่สวยงามและตอบสนองได้เร็วกว่าที่เคย ด้วยการทำความเข้าใจแผนผังวิดเจ็ต การเลือกประเภทวิดเจ็ตที่เหมาะสม การใช้ประโยชน์จากระบบโครงร่าง และการปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดด้านประสิทธิภาพ คุณสามารถสร้าง UI ที่ไม่เพียงแต่สวยงามสะดุดตาเท่านั้น แต่ยังมีประสิทธิภาพและบำรุงรักษาได้อีกด้วย
ไม่ว่าคุณจะสร้างต้นแบบอย่างง่ายหรือแอปพลิเคชันการผลิตที่ซับซ้อน ชุดเครื่องมือ UI ของ Flutter จะปรับขนาดตามความต้องการของคุณ เริ่มต้นด้วยองค์ประกอบที่เรียบง่าย ใช้ประโยชน์จากแค็ตตาล็อกวิดเจ็ตที่กว้างขวาง และใช้รูปแบบขั้นสูงมากขึ้นเรื่อยๆ เมื่อแอปพลิเคชันของคุณเติบโตขึ้น การผสมผสานระหว่างการทำซ้ำอย่างรวดเร็วผ่านการรีโหลดแบบร้อนและ API ที่แสดงออกถึงอารมณ์ของ Flutter ทำให้การพัฒนา UI บนมือถือเป็นประสบการณ์ที่สนุกสนานอย่างแท้จริง