希望使用 Flutter 編寫移動應用的 SwiftUI 開發者應查閱本指南。本指南解釋瞭如何將現有的 SwiftUI 知識應用於 Flutter。

Flutter 是一個用於構建跨平臺應用的框架,它使用 Dart 程式語言。要了解 Dart 程式設計與 Swift 程式設計之間的一些差異,請參閱Swift 開發者學習 DartSwift 開發者瞭解 Flutter 併發

在用 Flutter 進行構建時,你的 SwiftUI 知識和經驗非常有價值。

Flutter 還在 iOS 和 macOS 上執行時對應用行為進行了多項調整。要了解具體方法,請參閱平臺適配

本文件可以用作一本食譜,你可以跳到最符合你需求的問題。本指南嵌入了示例程式碼。透過使用懸停或聚焦時出現的“在 DartPad 中開啟”按鈕,你可以在 DartPad 上開啟並執行一些示例。

概述

#

作為介紹,請觀看以下影片。它概述了 Flutter 在 iOS 上的工作原理以及如何使用 Flutter 構建 iOS 應用。

在新標籤頁中觀看 YouTube 影片:“Flutter for iOS developers”

Flutter 和 SwiftUI 程式碼描述了 UI 的外觀和工作方式。開發者將這種型別的程式碼稱為*宣告式框架*。

檢視 vs. 小部件

#

**SwiftUI** 將 UI 元件表示為*檢視*。你使用*修飾符*配置檢視。

swift
Text("Hello, World!") // <-- This is a View
  .padding(10)        // <-- This is a modifier of that View

**Flutter** 將 UI 元件表示為*小部件*。

檢視和小部件都只存在到需要更改時。這些語言將此屬性稱為*不變性*。SwiftUI 將 UI 元件屬性表示為檢視修飾符。相比之下,Flutter 將小部件用於 UI 元件及其屬性。

dart
Padding(                         // <-- This is a Widget
  padding: EdgeInsets.all(10.0), // <-- So is this
  child: Text("Hello, World!"),  // <-- This, too
)));

為了組合佈局,SwiftUI 和 Flutter 都將 UI 元件巢狀在一起。SwiftUI 巢狀檢視,而 Flutter 巢狀小部件。

佈局過程

#

**SwiftUI** 使用以下過程來佈局檢視

  1. 父檢視向其子檢視建議一個大小。
  2. 所有後續子檢視
    • 向*它們*的子檢視建議一個大小
    • 詢問該子檢視它想要什麼大小
  3. 每個父檢視以返回的大小渲染其子檢視。

**Flutter** 的過程有所不同

  1. 父小部件將約束傳遞給其子小部件。約束包括高度和寬度的最小值和最大值。

  2. 子小部件嘗試決定其大小。它對其自己的子小部件列表重複相同的過程

    • 它告知其子小部件子小部件的約束。
    • 它詢問其子小部件它希望是什麼大小。
  3. 父小部件佈局子小部件。

    • 如果請求的大小符合約束,則父小部件使用該大小。
    • 如果請求的大小不符合約束,則父小部件限制高度、寬度或兩者以適應其約束。

Flutter 與 SwiftUI 不同,因為父元件可以覆蓋子元件的所需大小。小部件不能擁有任何它想要的大小。它也無法知道或決定它在螢幕上的位置,因為其父元件做出該決定。

要強制子小部件以特定大小渲染,父小部件必須設定緊密約束。當其約束的最小大小值等於其最大大小值時,約束變為緊密。

在 **SwiftUI** 中,檢視可能會擴充套件到可用空間或將其大小限制為其內容的大小。**Flutter** 小部件的行為方式類似。

然而,在 Flutter 中,父小部件可以提供無邊界約束。無邊界約束將其最大值設定為無窮大。

dart
UnboundedBox(
  child: Container(
      width: double.infinity, height: double.infinity, color: red),
)

如果子小部件擴充套件且具有無邊界約束,Flutter 將返回溢位警告

dart
UnconstrainedBox(
  child: Container(color: red, width: 4000, height: 50),
)
When parents pass unbounded constraints to children, and the children are expanding, then there is an overflow warning.

要了解 Flutter 中約束的工作原理,請參閱理解約束

設計系統

#

因為 Flutter 針對多個平臺,所以你的應用不需要符合任何設計系統。儘管本指南以 Material 小部件為特色,但你的 Flutter 應用可以使用許多不同的設計系統

  • 自定義 Material 小部件
  • 社群構建的小部件
  • 你自己的自定義小部件
  • 遵循 Apple 人機介面指南的Cupertino 小部件

在新標籤頁中觀看 YouTube 影片:“Flutter's cupertino library for iOS developers”

如果你正在尋找一個具有自定義設計系統的優秀參考應用,請檢視 Wonderous

UI 基礎

#

本節涵蓋 Flutter 中 UI 開發的基礎知識,以及它與 SwiftUI 的比較。這包括如何開始開發應用、顯示靜態文字、建立按鈕、響應按壓事件、顯示列表、網格等。

入門

#

在 **SwiftUI** 中,你使用 App 來啟動你的應用。

swift
@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            HomePage()
        }
    }
}

另一個常見的 SwiftUI 實踐是將應用主體放在符合 View 協議的 struct 中,如下所示

swift
struct HomePage: View {
  var body: some View {
    Text("Hello, World!")
  }
}

要啟動你的 **Flutter** 應用,將你的應用例項傳遞給 runApp 函式。

dart
void main() {
  runApp(const MyApp());
}

App 是一個小部件。build 方法描述了它所代表的使用者介面部分。通常,你的應用會以 WidgetApp 類(例如 CupertinoApp)開始。

dart
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    // Returns a CupertinoApp that, by default,
    // has the look and feel of an iOS app.
    return const CupertinoApp(home: HomePage());
  }
}

HomePage 中使用的小部件可能以 Scaffold 類開始。Scaffold 為應用實現了一個基本的佈局結構。

dart
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(body: Center(child: Text('Hello, World!')));
  }
}

請注意 Flutter 如何使用 Center 小部件。SwiftUI 預設將其檢視內容渲染在其中心。Flutter 則並非總是如此。Scaffold 不會在螢幕中心渲染其 body 小部件。要使文字居中,請將其包裝在 Center 小部件中。要了解不同小部件及其預設行為,請檢視小部件目錄

新增按鈕

#

在 **SwiftUI** 中,你使用 Button 結構體來建立按鈕。

swift
Button("Do something") {
  // this closure gets called when your
  // button is tapped
}

要在 **Flutter** 中實現相同的結果,請使用 CupertinoButton

dart
CupertinoButton(
  onPressed: () {
    // This closure is called when your button is tapped.
  },
  const Text('Do something'),
),

**Flutter** 為你提供了各種具有預定義樣式的按鈕。 CupertinoButton 類來自 Cupertino 庫。Cupertino 庫中的小部件使用 Apple 的設計系統。

水平對齊元件

#

在 **SwiftUI** 中,堆疊檢視在設計佈局中扮演著重要角色。兩個獨立的結構允許你建立堆疊

  1. HStack 用於水平堆疊檢視

  2. VStack 用於垂直堆疊檢視

以下 SwiftUI 檢視將一個地球影像和文字新增到水平堆疊檢視中

swift
HStack {
  Image(systemName: "globe")
  Text("Hello, world!")
}

**Flutter** 使用 Row 而不是 HStack

dart
Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [Icon(CupertinoIcons.globe), Text('Hello, world!')],
),

Row 小部件在 children 引數中需要一個 List<Widget>mainAxisAlignment 屬性告訴 Flutter 如何在額外空間中定位子小部件。MainAxisAlignment.center 將子小部件定位在主軸的中心。對於 Row,主軸是水平軸。

垂直對齊元件

#

以下示例基於上一節中的示例。

在 **SwiftUI** 中,你使用 VStack 將元件排列成垂直柱。

swift
VStack {
  Image(systemName: "globe")
  Text("Hello, world!")
}

**Flutter** 使用與上一個示例相同的 Dart 程式碼,只是它將 Column 替換為 Row

dart
Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [Icon(CupertinoIcons.globe), Text('Hello, world!')],
),

顯示列表檢視

#

在 **SwiftUI** 中,你使用 List 基本元件來顯示專案序列。要顯示模型物件序列,請確保使用者可以識別你的模型物件。要使物件可識別,請使用 Identifiable 協議。

swift
struct Person: Identifiable {
  var name: String
}

var persons = [
  Person(name: "Person 1"),
  Person(name: "Person 2"),
  Person(name: "Person 3"),
]

struct ListWithPersons: View {
  let persons: [Person]
  var body: some View {
    List {
      ForEach(persons) { person in
        Text(person.name)
      }
    }
  }
}

這類似於 **Flutter** 傾向於構建其列表小部件的方式。Flutter 不需要列表項是可識別的。你設定要顯示的專案數量,然後為每個專案構建一個小部件。

dart
class Person {
  String name;
  Person(this.name);
}

final List<Person> items = [
  Person('Person 1'),
  Person('Person 2'),
  Person('Person 3'),
];

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(title: Text(items[index].name));
        },
      ),
    );
  }
}

Flutter 的列表有一些注意事項

  • ListView 小部件有一個構建器方法。這類似於 SwiftUI List 結構體中的 ForEach

  • ListViewitemCount 引數設定 ListView 顯示的專案數量。

  • itemBuilder 有一個索引引數,該引數將在零到 itemCount 減一之間。

前面的示例為每個專案返回了一個 ListTile 小部件。ListTile 小部件包括 heightfont-size 等屬性。這些屬性有助於構建列表。但是,Flutter 允許你返回幾乎任何代表你資料的小部件。

顯示網格

#

在 **SwiftUI** 中構建非條件網格時,你使用 GridGridRow

swift
Grid {
  GridRow {
    Text("Row 1")
    Image(systemName: "square.and.arrow.down")
    Image(systemName: "square.and.arrow.up")
  }
  GridRow {
    Text("Row 2")
    Image(systemName: "square.and.arrow.down")
    Image(systemName: "square.and.arrow.up")
  }
}

要在 **Flutter** 中顯示網格,請使用 GridView 小部件。此小部件有各種建構函式。每個建構函式都有相似的目標,但使用不同的輸入引數。以下示例使用 .builder() 初始化器

dart
const widgets = [
  Text('Row 1'),
  Icon(CupertinoIcons.arrow_down_square),
  Icon(CupertinoIcons.arrow_up_square),
  Text('Row 2'),
  Icon(CupertinoIcons.arrow_down_square),
  Icon(CupertinoIcons.arrow_up_square),
];

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GridView.builder(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          mainAxisExtent: 40,
        ),
        itemCount: widgets.length,
        itemBuilder: (context, index) => widgets[index],
      ),
    );
  }
}

SliverGridDelegateWithFixedCrossAxisCount 委託確定網格用於佈局其元件的各種引數。這包括 crossAxisCount,它指定每行顯示的專案數量。

SwiftUI 的 Grid 和 Flutter 的 GridView 的區別在於 Grid 需要 GridRowGridView 使用委託來決定網格應該如何佈局其元件。

建立滾動檢視

#

在 **SwiftUI** 中,你使用 ScrollView 來建立自定義滾動元件。以下示例以可滾動的方式顯示一系列 PersonView 例項。

swift
ScrollView {
  VStack(alignment: .leading) {
    ForEach(persons) { person in
      PersonView(person: person)
    }
  }
}

要建立滾動檢視,**Flutter** 使用 SingleChildScrollView。在以下示例中,函式 mockPerson 模擬 Person 類的例項以建立自定義 PersonView 小部件。

dart
SingleChildScrollView(
  child: Column(
    children: mockPersons
        .map((person) => PersonView(person: person))
        .toList(),
  ),
),

響應式和自適應設計

#

在 **SwiftUI** 中,你使用 GeometryReader 來建立相對檢視大小。

例如,你可以

  • geometry.size.width 乘以某個因子來設定*寬度*。
  • 使用 GeometryReader 作為斷點來改變應用的設計。

你還可以使用 horizontalSizeClass 檢視大小類是否為 .regular.compact

要在 **Flutter** 中建立相對檢視,你可以使用以下兩種選項之一

  • LayoutBuilder 類中獲取 BoxConstraints 物件。
  • 在你的構建函式中使用 MediaQuery.of() 來獲取當前應用的大小和方向。

要了解更多資訊,請檢視建立響應式和自適應應用

狀態管理

#

在 **SwiftUI** 中,你使用 @State 屬性包裝器來表示 SwiftUI 檢視的內部狀態。

swift
struct ContentView: View {
  @State private var counter = 0;
  var body: some View {
    VStack{
      Button("+") { counter+=1 }
      Text(String(counter))
    }
  }}

**SwiftUI** 還包括幾種用於更復雜狀態管理的選項,例如 ObservableObject 協議。

**Flutter** 使用 StatefulWidget 管理本地狀態。使用以下兩個類實現有狀態小部件

  • StatefulWidget 的子類
  • State 的子類

State 物件儲存小部件的狀態。要更改小部件的狀態,從 State 子類呼叫 setState() 以告知框架重新繪製小部件。

以下示例展示了計數器應用的一部分

dart
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$_counter'),
            TextButton(
              onPressed: () => setState(() {
                _counter++;
              }),
              child: const Text('+'),
            ),
          ],
        ),
      ),
    );
  }
}

要了解更多管理狀態的方法,請檢視狀態管理

動畫

#

存在兩種主要型別的 UI 動畫。

  • 隱式動畫:從當前值動畫到新目標。
  • 顯式動畫:在被要求時進行動畫。

隱式動畫

#

SwiftUI 和 Flutter 採用相似的動畫方法。在這兩個框架中,你都指定了諸如 durationcurve 之類的引數。

在 **SwiftUI** 中,你使用 animate() 修飾符來處理隱式動畫。

swift
Button("Tap me!"){
   angle += 45
}
.rotationEffect(.degrees(angle))
.animation(.easeIn(duration: 1))

**Flutter** 包含用於隱式動畫的小部件。這簡化了常見小部件的動畫。Flutter 以以下格式命名這些小部件:AnimatedFoo

例如:要旋轉按鈕,請使用 AnimatedRotation 類。這將對 Transform.rotate 小部件進行動畫處理。

dart
AnimatedRotation(
  duration: const Duration(seconds: 1),
  turns: turns,
  curve: Curves.easeIn,
  TextButton(
    onPressed: () {
      setState(() {
        turns += .125;
      });
    },
    const Text('Tap me!'),
  ),
),

Flutter 允許你建立自定義隱式動畫。要組合新的動畫小部件,請使用 TweenAnimationBuilder

顯式動畫

#

對於顯式動畫,**SwiftUI** 使用 withAnimation() 函式。

**Flutter** 包含名稱格式為 FooTransition 的顯式動畫小部件。一個示例是 RotationTransition 類。

Flutter 還允許你使用 AnimatedWidgetAnimatedBuilder 建立自定義顯式動畫。

要了解有關 Flutter 中動畫的更多資訊,請參閱動畫概述

在螢幕上繪圖

#

在 **SwiftUI** 中,你使用 CoreGraphics 在螢幕上繪製線條和形狀。

**Flutter** 有一個基於 Canvas 類的 API,其中有兩個類可以幫助你繪製

  1. 需要繪製器的 CustomPaint

    dart
    CustomPaint(
      painter: SignaturePainter(_points),
      size: Size.infinite,
    ),
  2. 實現你的演算法以繪製到畫布的 CustomPainter

    dart
    class SignaturePainter extends CustomPainter {
      SignaturePainter(this.points);
    
      final List<Offset?> points;
    
      @override
      void paint(Canvas canvas, Size size) {
        final Paint paint = Paint()
          ..color = Colors.black
          ..strokeCap = StrokeCap.round
          ..strokeWidth = 5;
        for (int i = 0; i < points.length - 1; i++) {
          if (points[i] != null && points[i + 1] != null) {
            canvas.drawLine(points[i]!, points[i + 1]!, paint);
          }
        }
      }
    
      @override
      bool shouldRepaint(SignaturePainter oldDelegate) =>
          oldDelegate.points != points;
    }
#

本節解釋瞭如何在應用頁面之間導航、推入和彈出機制等等。

#

開發者使用稱為*導航路由*的不同頁面構建 iOS 和 macOS 應用。

在 **SwiftUI** 中,NavigationStack 代表了這種頁面堆疊。

以下示例建立了一個顯示人員列表的應用。要在新導航連結中顯示人員的詳細資訊,請點選該人員。

swift
NavigationStack(path: $path) {
      List {
        ForEach(persons) { person in
          NavigationLink(
            person.name,
            value: person
          )
        }
      }
      .navigationDestination(for: Person.self) { person in
        PersonView(person: person)
      }
    }

如果你的 **Flutter** 應用很小且沒有複雜的連結,請使用 Navigator 和命名路由。定義導航路由後,使用它們的名稱呼叫你的導航路由。

  1. 在傳遞給 runApp() 函式的類中命名每個路由。以下示例使用 App

    dart
    // Defines the route name as a constant
    // so that it's reusable.
    const detailsPageRouteName = '/details';
    
    class App extends StatelessWidget {
      const App({super.key});
    
      @override
      Widget build(BuildContext context) {
        return CupertinoApp(
          home: const HomePage(),
          // The [routes] property defines the available named routes
          // and the widgets to build when navigating to those routes.
          routes: {detailsPageRouteName: (context) => const DetailsPage()},
        );
      }
    }

    以下示例使用 mockPersons() 生成人員列表。點選一個人會將該人員的詳細資訊頁面使用 pushNamed() 推入 Navigator

    dart
    ListView.builder(
      itemCount: mockPersons.length,
      itemBuilder: (context, index) {
        final person = mockPersons.elementAt(index);
        final age = '${person.age} years old';
        return ListTile(
          title: Text(person.name),
          subtitle: Text(age),
          trailing: const Icon(Icons.arrow_forward_ios),
          onTap: () {
            // When a [ListTile] that represents a person is
            // tapped, push the detailsPageRouteName route
            // to the Navigator and pass the person's instance
            // to the route.
            Navigator.of(
              context,
            ).pushNamed(detailsPageRouteName, arguments: person);
          },
        );
      },
    ),
  2. 定義顯示每個人詳細資訊的 DetailsPage 小部件。在 Flutter 中,你可以在導航到新路由時將引數傳遞到小部件中。使用 ModalRoute.of() 提取引數

    dart
    class DetailsPage extends StatelessWidget {
      const DetailsPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        // Read the person instance from the arguments.
        final Person person = ModalRoute.of(context)?.settings.arguments as Person;
        // Extract the age.
        final age = '${person.age} years old';
        return Scaffold(
          // Display name and age.
          body: Column(children: [Text(person.name), Text(age)]),
        );
      }
    }

要建立更高階的導航和路由要求,請使用路由包,例如 go_router

要了解更多資訊,請檢視導航和路由

手動返回

#

在 **SwiftUI** 中,你使用 dismiss 環境值來返回到上一個螢幕。

swift
Button("Pop back") {
        dismiss()
      }

在 **Flutter** 中,使用 Navigator 類的 pop() 函式

dart
TextButton(
  onPressed: () {
    // This code allows the
    // view to pop back to its presenter.
    Navigator.of(context).pop();
  },
  child: const Text('Pop back'),
),
#

在 **SwiftUI** 中,你使用 openURL 環境變數來開啟 URL 到另一個應用程式。

swift
@Environment(\.openURL) private var openUrl

// View code goes here

 Button("Open website") {
      openUrl(
        URL(
          string: "https://google.com"
        )!
      )
    }

在 **Flutter** 中,使用 url_launcher 外掛。

dart
CupertinoButton(
  onPressed: () async {
    await launchUrl(Uri.parse('https://google.com'));
  },
  const Text('Open website'),
),

主題、樣式和媒體

#

你可以毫不費力地為 Flutter 應用設定樣式。樣式包括在亮色和深色主題之間切換、更改文字和 UI 元件的設計等等。本節介紹瞭如何為你的應用設定樣式。

使用深色模式

#

在 **SwiftUI** 中,你在 View 上呼叫 preferredColorScheme() 函式以使用深色模式。

在 **Flutter** 中,你可以在應用級別控制亮色和深色模式。要控制亮度模式,請使用 App 類的 theme 屬性

dart
const CupertinoApp(
  theme: CupertinoThemeData(brightness: Brightness.dark),
  home: HomePage(),
);

文字樣式

#

在 **SwiftUI** 中,你使用修飾符函式來設定文字樣式。例如,要更改 Text 字串的字型,請使用 font() 修飾符

swift
Text("Hello, world!")
  .font(.system(size: 30, weight: .heavy))
  .foregroundColor(.yellow)

要在 **Flutter** 中設定文字樣式,請將 TextStyle 小部件作為 Text 小部件的 style 引數的值。

dart
Text(
  'Hello, world!',
  style: TextStyle(
    fontSize: 30,
    fontWeight: FontWeight.bold,
    color: CupertinoColors.systemYellow,
  ),
),

按鈕樣式

#

在 **SwiftUI** 中,你使用修飾符函式來設定按鈕樣式。

swift
Button("Do something") {
    // do something when button is tapped
  }
  .font(.system(size: 30, weight: .bold))
  .background(Color.yellow)
  .foregroundColor(Color.blue)
}

要在 **Flutter** 中設定按鈕小部件的樣式,請設定其子項的樣式,或修改按鈕本身的屬性。

在以下示例中

  • CupertinoButtoncolor 屬性設定其 color
  • Text 小部件的 color 屬性設定按鈕文字顏色。
dart
child: CupertinoButton(
  color: CupertinoColors.systemYellow,
  onPressed: () {},
  padding: const EdgeInsets.all(16),
  child: const Text(
    'Do something',
    style: TextStyle(
      color: CupertinoColors.systemBlue,
      fontSize: 30,
      fontWeight: FontWeight.bold,
    ),
  ),
),

使用自定義字型

#

在 **SwiftUI** 中,你可以透過兩個步驟在應用中使用自定義字型。首先,將字型檔案新增到你的 SwiftUI 專案中。新增檔案後,使用 .font() 修飾符將其應用於你的 UI 元件。

swift
Text("Hello")
  .font(
    Font.custom(
      "BungeeSpice-Regular",
      size: 40
    )
  )

在 **Flutter** 中,你使用名為 pubspec.yaml 的檔案控制你的資源。此檔案與平臺無關。要將自定義字型新增到你的專案,請按照以下步驟操作

  1. 在專案的根目錄中建立一個名為 fonts 的資料夾。此可選步驟有助於組織你的字型。

  2. 將你的 .ttf.otf.ttc 字型檔案新增到 fonts 資料夾中。

  3. 開啟專案中的 pubspec.yaml 檔案。

  4. 找到 flutter 部分。

  5. fonts 部分下新增你的自定義字型。

    yaml
    flutter:
      fonts:
        - family: BungeeSpice
          fonts:
            - asset: fonts/BungeeSpice-Regular.ttf

將字型新增到你的專案後,你可以按照以下示例使用它

dart
Text(
  'Cupertino',
  style: TextStyle(fontSize: 40, fontFamily: 'BungeeSpice'),
),

在應用中捆綁圖片

#

在 **SwiftUI** 中,你首先將影像檔案新增到 Assets.xcassets,然後使用 Image 檢視來顯示影像。

要在 **Flutter** 中新增影像,請遵循與新增自定義字型類似的方法。

  1. 在根目錄中新增一個 images 資料夾。

  2. 將此資產新增到 pubspec.yaml 檔案。

    yaml
    flutter:
      assets:
        - images/Blueberries.jpg

新增影像後,使用 Image 小部件的 .asset() 建構函式顯示它。此建構函式

  1. 使用提供的路徑例項化給定的影像。
  2. 從與你的應用捆綁的資產中讀取影像。
  3. 在螢幕上顯示影像。

要檢視完整示例,請檢視 Image 文件。

在應用中捆綁影片

#

在 **SwiftUI** 中,你透過兩個步驟將本地影片檔案捆綁到你的應用中。首先,你匯入 AVKit 框架,然後例項化一個 VideoPlayer 檢視。

在 **Flutter** 中,將 video_player 外掛新增到你的專案。此外掛允許你建立一個可在 Android、iOS 和 Web 上使用相同程式碼庫執行的影片播放器。

  1. 將外掛新增到你的應用並將影片檔案新增到你的專案。
  2. 將資產新增到你的 pubspec.yaml 檔案。
  3. 使用 VideoPlayerController 類載入和播放你的影片檔案。

要檢視完整的演練,請檢視 video_player 示例