Flutter開發 - 使用GetX框架實現類似MVVM架構
開啟掘金成長之旅!這是我參與「掘金日新計劃 · 2 月更文挑戰」的第 7 天,點擊查看活動詳情
回顧原生開發
在Android原生開發中,通常是使用Databinding實現MVVM架構,只需要在gradle中開啟databinding的選項,然後使用ObservableField或LiveData即可。
groovy
buildFeatures {
dataBinding true
}
xml
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="m" type="com.example.vm.LoginViewModel" />
</data>
<EditText android:text="@{m.username}"/>
</layout>
在ViewModel中可以定義ObservableField,這樣界面就可以直接觀察了。簡單看一下ObservableField的源碼,其實還有另外的幾個類也是類似的功能,比如ObservableInt和ObservableBoolean。
```java package androidx.databinding;
import androidx.annotation.Nullable;
import java.io.Serializable;
/* * An object wrapper to make it observable. *
* Observable field classes may be used instead of creating an Observable object. It can also * create a calculated field, depending on other fields: *
public class MyDataObject {
* private Context context;
* public final ObservableField<String> first = new ObservableField<String>();
* public final ObservableField<String> last = new ObservableField<String>();
* public final ObservableField<String> display =
* new ObservableField<String>(first, last) {
* @Override
* public String get() {
* return context.getResources().getString(R.string.name, first.get, last.get());
* }
* };
* public final ObservableInt age = new ObservableInt();
* }
* Fields of this type should be declared final because bindings only detect changes in the
* field's value, not of the field itself.
*
* @param /**
* Wraps the given object and creates an observable object
*
* @param value The value to be wrapped as an observable.
*/
public ObservableField(T value) {
mValue = value;
}
/**
* Creates an empty observable object
*/
public ObservableField() {
}
/**
* Creates an ObservableField that depends on {@code dependencies}. Typically,
* ObservableFields are passed as dependencies. When any dependency
* notifies changes, this ObservableField also notifies a change.
*
* @param dependencies The Observables that this ObservableField depends on.
*/
public ObservableField(Observable... dependencies) {
super(dependencies);
}
/**
* @return the stored value.
*/
@Nullable
public T get() {
return mValue;
}
/**
* Set the stored value.
*
* @param value The new value
*/
public void set(T value) {
if (value != mValue) {
mValue = value;
notifyChange();
}
}
} ``` databinding裏面的ObservableField類可以幫助我們很好的實現數據的雙向綁定,通過調用它 的get()方法就可以拿到值了,在xml中也可以很方便地使用其值。 這樣一種優雅的寫法,在Flutter中怎麼玩呢?還是那句話,巧婦難為無米之炊,首先你得有Flutter的開發環境,要不然你也就只能看看了。如果不瞭解Flutter環境怎麼搭建的可以看https://juejin.cn/post/7185571569048125477 這篇文章。那麼我們開向幼兒園的車馬上就要發車了。不好意思,説錯了,是開往大前端大佬的車。
框架搭建
我們先大概瞭解下Flutter項目的項目結構,由於這是一個演示Demo,我這裏就不分包了,你們可以寫的更優雅。集成get框架就一個命令,flutter就是這麼簡單。
flutter pub add get
內行人一眼就看出我們肯定是先看main.dart文件。
```dart import 'package:flutter/material.dart'; import 'package:flutter_mvvm_demo/home_binding.dart'; import 'package:flutter_mvvm_demo/main_binding.dart'; import 'package:flutter_mvvm_demo/home_view.dart'; import 'package:flutter_mvvm_demo/main_view.dart'; import 'package:get/get.dart';
void main() { runApp(const MyApp()); }
class MyApp extends StatelessWidget { const MyApp({super.key});
@override Widget build(BuildContext context) { return GetMaterialApp( initialRoute: '/home', getPages: [ GetPage( name: '/home', page: () => const HomeView(), binding: HomeBinding(), ), GetPage( name: '/main', page: () => const MainView(), binding: MainBinding(), ), ], ); } } ``` 這個MyApp就是我們程序的入口,相當於Application類,其實最主要是因為在main()方法中調了一個runApp()方法,然後傳入了我們的第一個視圖。在Flutter中,不是管View叫視圖。Flutter中的視圖有兩種,一種是有狀態的StatefulWidget,還有一種是無狀態的StatelessWidget。我們這裏返回了一個GetMaterialApp,裏面指定了兩個屬性。initialRoute代表第一個頁面的路由,getPages中指定所有的頁面。GetPage裏面的page指定了這個頁面的視圖Widget,然後使用binding指定依賴注入,簡單説就是不用在使用的時候new對象,直接注入到屬性中。
```dart import 'package:flutter_mvvm_demo/home_controller.dart'; import 'package:get/get.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.put(HomeController());
}
}
dart
import 'package:flutter_mvvm_demo/main_controller.dart';
import 'package:get/get.dart';
class MainBinding extends Bindings {
@override
void dependencies() {
Get.put(MainController());
}
}
調用Get.put()方法我們就注入了controller,之後我們可以直接在視圖層拿到這個controller。
dart
import 'package:flutter/material.dart';
import 'package:flutter_mvvm_demo/home_controller.dart';
import 'package:get/get.dart';
class HomeView extends GetView
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(onPressed: () {
Get.toNamed('/main');
}, child: const Text('跳轉主界面')) ,
);
}
}
dart
import 'package:flutter/material.dart';
import 'package:flutter_mvvm_demo/main_controller.dart';
import 'package:get/get.dart';
class MainView extends GetView
@override
Widget build(BuildContext context) {
return Obx(() => Row(children: [
Text('${controller.count}'),
ElevatedButton(onPressed: () {
controller.plus();
}, child: const Text('+'))
]));
}
}
視圖層我們直接繼承GetView比較方便,這樣可以直接拿到之前注入的controller。使用Obx括起來的內容,當被觀察的屬性值被修改時,直接更新Obx裏面的界面。我們再來看一看怎麼定義可以被觀察的屬性,類似於原生開發中ObservableField<String>這樣的。
dart
import 'package:get/get.dart';
class MainController extends GetxController {
var count = 0.obs;
plus() => count++; } ``` 你沒有看錯,直接在屬性值的地方加一個.obs就可以了。這樣count的值一旦被修改,就會通知刷新Obs括起來的界面。
效果演示
- Android性能優化之內存優化
- 區塊鏈到底能不能落地,去中心化有沒有必要
- Flutter開發 - 使用GetX框架實現類似MVVM架構
- Android電量優化,讓你的手機續航更持久
- Android對Firebase崩潰日誌和埋點分析的集成
- Android代碼實現新年賀卡動畫
- Android應用添加谷歌登錄(Google Sign In)
- Android自定義View - DoraEmptyLayout
- 全新升級的AOP框架Dora.Interception[6]: 框架設計和實現原理
- 如何高效讀寫百萬級的Excel?
- 紅人姜Dora:從雙非二本到頂級互聯網大廠,一個普通人的升級打怪之路
- 頭等倉:深度解析波卡生態 DAO 基礎設施 Dora Factory
- 6年,12dora終渡B劫
- 6年,12dora終渡B劫