ข้ามไปยังเนื้อหา

บล็อก

การพัฒนา UI บนมือถืออย่างรวดเร็วด้วย Flutter: คู่มือที่ครอบคลุม

คำแนะนำที่ครอบคลุมเกี่ยวกับการสร้างอินเทอร์เฟซมือถือที่น่าทึ่งด้วยชุดเครื่องมือวิดเจ็ตอันทรงพลังของ Flutter

  • อ่าน 5 นาที
  • Updated

Flutter ได้พลิกโฉมภูมิทัศน์การพัฒนาอุปกรณ์เคลื่อนที่โดยช่วยให้นักพัฒนาสามารถสร้างแอปพลิเคชันที่สวยงามและคอมไพล์แล้วสำหรับอุปกรณ์เคลื่อนที่ เว็บ และเดสก์ท็อปจากโค้ดเบสเดียว ด้วยไลบรารีวิดเจ็ตที่หลากหลาย เฟรมเวิร์ก UI ที่ชัดเจน และฟีเจอร์การโหลดซ้ำยอดนิยม Flutter ช่วยให้ทีมสามารถทำซ้ำได้อย่างรวดเร็วและส่งมอบอินเทอร์เฟซผู้ใช้ที่สวยงามในเวลาที่บันทึก

ในคู่มือที่ครอบคลุมนี้ เราจะสำรวจแนวคิดหลักที่ทำให้ Flutter เป็นตัวเลือกที่ยอดเยี่ยมสำหรับการพัฒนา UI บนมือถืออย่างรวดเร็ว ตั้งแต่การทำความเข้าใจแผนผังวิดเจ็ตไปจนถึงการนำภาพเคลื่อนไหวที่ซับซ้อนและเค้าโครงที่ตอบสนองไปใช้

การทำความเข้าใจแผนผังวิดเจ็ต Flutter

ทุกอย่างใน Flutter นั้นเป็นวิดเจ็ต แอปพลิเคชันทั้งหมดถูกสร้างขึ้นเป็นแผนผังวิดเจ็ต โดยแต่ละวิดเจ็ตจะอธิบายส่วนหนึ่งของอินเทอร์เฟซผู้ใช้ วิธีการประกาศนี้หมายความว่าคุณอธิบายว่า UI ควรมีลักษณะอย่างไรสำหรับสถานะที่กำหนด และ Flutter จะดูแลการอัปเดตการเรนเดอร์เมื่อสถานะเปลี่ยนแปลง

Flutter สถาปัตยกรรมข้ามแพลตฟอร์มCodebase เดี่ยวDart + Flutter วิดเจ็ตตรรกะทางธุรกิจที่ใช้ร่วมกัน & amp; UIiOSiPhone / iPadAndroidโทรศัพท์ / แท็บเล็ตเว็บChrome / Safariเดสก์ท็อปmacOS / Windows~95% รหัสที่ใช้ร่วมกันข้ามแพลตฟอร์ม

แผนผังวิดเจ็ตเป็นแบบลำดับชั้น ที่ด้านบนจะมี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 ที่กำลังทำงานอยู่ โดยคงสถานะปัจจุบันของแอปของคุณ ซึ่งหมายความว่าคุณสามารถปรับแต่งช่องว่างภายในของวิดเจ็ต เปลี่ยนสี หรือปรับโครงสร้างเค้าโครง และดูผลลัพธ์ได้ภายในไม่กี่วินาที

Flutter เวิร์กโฟลว์โหลดซ้ำร้อนแก้ไขโค้ด.dart ไฟล์ที่บันทึกไว้⚡ โหลดซ้ำร้อน< 1 วิ ตรวจสอบวนซ้ำ → วงจรการพัฒนาอย่างรวดเร็วห่วงข้อเสนอแนะย่อยวินาทีเร็วกว่าการคอมไพล์ดั้งเดิม 10 เท่า

เพื่อเพิ่มประสิทธิภาพการโหลดซ้ำร้อน ให้ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเหล่านี้:

  • เก็บวิธีการสร้างบริสุทธิ์ - วิธีการสร้างควรทำเท่านั้น ขึ้นอยู่กับสถานะของวิดเจ็ตและวิดเจ็ตที่สืบทอดมา ผลข้างเคียงในวิธีการ 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 บนมือถือเป็นประสบการณ์ที่สนุกสนานอย่างแท้จริง