Flutter 中的物件導向和路由


筆記資源

UI / UX

UI:USER INTERFACE 使用者介面 UX:USER EXPERIENCE 使用者體驗

看的到的是UI,像是介面設計,抱括按鈕、圖示、排版、間距等設計。 體驗到的是UX,像是功能使用上的互動,能不能有良好的體驗。

兩者皆需要註冊帳號 Figma官網 Flutter Flow 官網

沒有好或壞的UI Figma好處

OOP

codelabs 物件導向是一種在設計程式的一種方式 但不是每一種程式語言都有這種概念 像是C語言就沒有,但在這之上的 C++、Objective-C就有物件導向 其他如 Java以及等等要介紹的 Dart

最容易判斷是不是物件導向只要認得 Class 類別這個概念

DartPad 使用線上編譯器來學習吧

Dart 沒有 Private 屬性 要設定唯獨變數要在變數名稱前面加底線_ 例如 String _name;

在呼叫類別的時候,建構子引數可以直接使用 this 快速指定變數到類別中

在實力化物件的時候可以省略 new 關鍵字

其他更詳細的介紹

ROUTE

路由有兩種,一種是直接呼叫介面;另一種是幫每一頁命名

步驟

  1. 創建兩個頁面,分別為 'first_screen.dart' 與 'second_screen.dart',參考下方的程式,建立Widget的基本架構

第一個頁面

import 'package:flutter/material.dart'; class FirstScreen extends StatelessWidget { const FirstScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('First Screen'), ), body: Center( child: ElevatedButton( onPressed: () { //TODO }, child: const Text('Launch screen'), ), ), ); } }

這個筆記裡有BTN的介紹

第二個頁面

import 'package:flutter/material.dart'; // 第二個頁面 class SecondScreen extends StatelessWidget { const SecondScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { //TODO return Scaffold( appBar: AppBar( title: const Text('Second Screen'), ), body: Column( children: [ //TODO Center( child: ElevatedButton( onPressed: () { //TODO }, child: const Text('Go back!'), ), ), ], )); } }

標記TODO的地方,請暫時忽略它,稍後將繼續完善裡面的程式

以下方法是定義路由名稱跳轉的方式

  1. 在 'main.dart' 中定義路由

  2. 在 'first_screen.dart' 中的 onPressed 使用 Navigator.pushNamed() 跳轉到頁面二

  3. 在 'second_screen.dart' 中的 onPressed 使用 Navigator.pop() 返回頁面一

  4. 讓我開始頁面間傳送一些參數,我們想要傳送三個東西分別為 ID, Title, Imgurl 到第二個頁面,我們需要在頁面一 'first_screen.dart' 中定義要傳入的參數

  5. 在第二個頁面 'second_screen.dart' 定義這些要傳入的參數

  6. 我們可以使用這些參數了,我們用 Text 與 Image 配合 Column 來顯示傳進來的內容

import 'package:flutter/material.dart'; // 第二個頁面 class SecondScreen extends StatelessWidget { const SecondScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final routeArgs = ModalRoute.of(context)?.settings.arguments as Map<String, String>; final categoryTitle = routeArgs['title']; final categoryId = routeArgs['id']; final categoryImg = routeArgs['imgUrl']; return Scaffold( appBar: AppBar( title: const Text('Second Screen'), ), body: Column( children: [ Text(categoryTitle ?? 'Nothing from the screen one'), Text(categoryId ?? '?'), Image.network(categoryImg ?? ''), Center( child: ElevatedButton( onPressed: () { // 通過從堆疊弹出當前路由 Navigator.of(context).pop(); }, child: const Text('Go back!'), ), ), ], )); } }

🧠 Text、Image、Column 的用法,上次已經介紹過,忘記的請參考之前的筆記

完整程式 DartPad 可以直接點這裡看

  1. first_screen.dart
// import 'package:flutter/material.dart'; // 在AndroidStudio中記得要 import material(上面這行) // 第一個頁面 class FirstScreen extends StatelessWidget { const FirstScreen({Key? key}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('First Screen'), ), body: Center( child: ElevatedButton( onPressed: () { // 使用命名路由跳轉到第二個界面 Navigator.of(context).pushNamed( '/second', // 第二頁名稱 arguments: { 'id': 'Krabby Patty', 'title': 'From Screen One', 'imgUrl': 'https://i.imgur.com/sbOoRM2.gif', }, // 傳參數到下一頁 ); }, child: const Text('Launch screen'), ), ), ); } }
  1. second_screen.dart
// import 'package:flutter/material.dart'; // 在AndroidStudio中記得要 import material(上面這行) // 第二個頁面 class SecondScreen extends StatelessWidget { const SecondScreen({Key? key}) : super(key: key); Widget build(BuildContext context) { final routeArgs = ModalRoute.of(context)?.settings.arguments as Map<String, String>; final categoryTitle = routeArgs['title']; final categoryId = routeArgs['id']; final categoryImg = routeArgs['imgUrl']; return Scaffold( appBar: AppBar( title: const Text('Second Screen'), ), body: Column( children: [ Text(categoryTitle ?? 'Nothing from the screen one'), Text(categoryId ?? '?'), Image.network(categoryImg ?? ''), Center( child: ElevatedButton( onPressed: () { // 通過從堆疊弹出當前路由 Navigator.of(context).pop(); }, child: const Text('Go back!'), ), ), ], )); } }
  1. main.dart
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. Widget build(BuildContext context) { return MaterialApp( title: 'Named Routes Demo', // 使用“/”命名路由来啟動程式 // 在這裡,程式將從 FirstScreen Widget 啟動 initialRoute: '/', //'***名稱***': (context) => const ***頁面 Widget 名稱*** routes: { '/': (context) => const FirstScreen(), '/second': (context) => const SecondScreen(), }, ); } }

print(A ?? B)=> if A == NULL print(B) else print(A) 影片詳細教學

导航到对应名称的 routes 里 | Flutter 中文文档 | Flutter 中文开发者网站 (更多跳轉參數)

Flutter中管理路由栈的方法和应用 - 知乎

路由管理 | 《Flutter实战·第二版》


main.dart

import 'package:flutter/material.dart'; import 'package:route/second_page.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); // This widget is the root of your application. Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', debugShowCheckedModeBanner: false, initialRoute: '/', routes: { '/': (context) => const FirstPage(), '/second': (context) => const SecondPage() }, theme: ThemeData( // 設定不同的主題讓UI更好看 primarySwatch: Colors.blue, appBarTheme: const AppBarTheme(color: Colors.amber), useMaterial3: true), ); } } class FirstPage extends StatelessWidget { const FirstPage({Key? key}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Page 1'), ), body: Center( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.blueAccent, foregroundColor: Colors.white), onPressed: () { // 使用 MaterialPageRoute 跳轉頁面 // Navigator.of(context) // .push(MaterialPageRoute(builder: (context) => SecondPage())); // 使用命名規則跳轉 並傳入 List argument Navigator.of(context).pushNamed('/second', arguments: ['青麥香', '彩虹堂', '金香堡', '莫尼', '緣來', '尼好']); }, child: const Text('Go To Second Page'), ), ), ); } }

second_page.dart

import 'dart:math'; import 'package:flutter/material.dart'; class SecondPage extends StatefulWidget { const SecondPage({Key? key}) : super(key: key); State<SecondPage> createState() => _SecondPageState(); } class _SecondPageState extends State<SecondPage> { int random = 0; // 定義 random index 變數 Widget build(BuildContext context) { // 接收傳入的變數 指定為 List 型態 final routeArgs = ModalRoute.of(context)!.settings.arguments as List; return Scaffold( appBar: AppBar( title: const Text('早餐吃什麼?'), ), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( border: Border.all(width: 5, color: Colors.black87), borderRadius: BorderRadius.circular(24)), child: Column( mainAxisAlignment: MainAxisAlignment.center, // Column 置中參數設定 children: [ Text( routeArgs[random]!, style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold), ), const SizedBox( height: 24, ), ElevatedButton( style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 24), backgroundColor: Colors.purple, foregroundColor: Colors.white), onPressed: () { // Navigator.of(context).pop(); // 關閉 SecondPage() setState(() { random = Random().nextInt(routeArgs.length); }); }, child: const Text('Random', style: TextStyle(fontSize: 18),), ), Text('BXXXXXXX GDSC NIU') ], ), ), ), ); } }

成果幫我截圖放這裡!!

範例