iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Mobile Development

Flutter - 複製貼上到開發套件之旅系列 第 11

【第十一天 - Flutter GetX 架構教學】

前言

今日的程式碼 => GIHUB

GetX 介紹

GetX 官方文件

GetX 是一個很神奇的狀態管理,裡面有路由、語言、網路、還有一些內建的方法。我個人覺得他可以使用簡短的程式碼,達到想要的效果。可以看到下面的這幾張圖片。
NavigationDialogSnackBar、便利的資料傳遞、快速的儲存資料、簡單的國際化語言、簡單的改片 App 顏色、快速的驗證。
他可以快速的分享資料、狀態等資訊,因此他很強大。
https://raw.githubusercontent.com/jonataslaw/getx-community/master/getx.png
圖片來源 => https://raw.githubusercontent.com/jonataslaw/getx-community/master/getx.png

優勢

  • 可以不用寫 StreamController
  • 不需要為一個初始值建立一個 get
  • 不同頁面之間共享狀態
  • 可以使用 StatelessWidget 節省一些記憶體,使用 Get 你可能不再需要使用 StatefulWidget
  • 有許多內建的方法可以直接使用

缺點

  • 個人覺得他裡面包了太多神奇的東西,哪一天 flutter 改版了一下,GetX 可能就需要一些時間更新了
  • 小型專案很適合,可是大型專案,可能需要一些測試等等,因此可能會不太適合(ㄅ)
  • 會被 GetX 綁住

以上是小弟的見解,有誤,歡迎留言底下和我說~~

先來介紹 Api Service 的部分

首先 Model 的部分可以參考 【第七天 - Flutter Api、Json 物件教學】

GetConnect

GetConnect 是一個 GetX 提供給 Api 請求資料的一個類別。因此不需要去使用 Http 的套件了。

class PostService extends GetConnect {
//  請求 Api
  Future<List<PostModel>> fetchData() async {
    return await get(
      'https://jsonplaceholder.typicode.com/posts',
      decoder: (data) =>
      List<PostModel>.from(data.map((e) => PostModel.fromJson(e))),
    ).then((value) => value.body!).catchError((e) => throw e);
  }
}

GetxController 控制器

GetxController 是一個控制器,我們繼承他,就可以操作了。也不用把他想的那麼複雜。

.obs 的話,代表我們可以觀察那筆資料。當我們使用 update() 就可以通知畫面我們更新數據了。

enum SortState { userID, id, title, body }

class PostController extends GetxController {
  var isLoading = true.obs;
  var postList = <PostModel>[].obs;

  @override
  void onInit() {
    super.onInit();
    fetchApi();
  }

  void fetchApi() async {
    isLoading(true);
    await PostService().fetchData().then((value) {
      postList.assignAll(value);
      isLoading(false);
      update();
    }).catchError((e) {});
  }

  // sort method
  void sort(SortState sortState) async {
    switch (sortState) {
      case SortState.title:
        postList.sort((a, b) => a.title.compareTo(b.title));
        break;
      case SortState.id:
        postList.sort((a, b) => a.id.compareTo(b.id));
        break;
      case SortState.userID:
        postList.sort((a, b) => a.userId.compareTo(b.userId));
        break;
      case SortState.body:
        postList.sort((a, b) => a.body.compareTo(b.body));
        break;
    }
    update();
  }
}

使用方式

如果資料都有 .obs 的話,我們在 UI 的部分就可以直接 body: Obx(() => Widget) 的方式直接拿到數據。

初始化 controller 使你其對當下的所有子路由可用。

final PostController controller = Get.put(PostController());

你可以找到一個正在被其他頁面使用的 Controller

Get.find<PostController>().sort(value);

View(畫面、UI)

HomePage

class HomePage extends StatelessWidget {
  HomePage({Key? key, required this.title}) : super(key: key);
  final String title;
  final PostController controller = Get.put(PostController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: <Widget>[
          PopupMenuButton<SortState>(
            icon: Icon(Icons.more_vert),
            itemBuilder: (context) => [
              PopupMenuItem(
                child: Text('使用 userId 排序'),
                value: SortState.userID,
              ),
              PopupMenuItem(
                child: Text('使用 id 排序'),
                value: SortState.id,
              ),
              PopupMenuItem(
                child: Text('使用 title 排序'),
                value: SortState.title,
              ),
              PopupMenuItem(
                child: Text('使用 body 排序'),
                value: SortState.body,
              )
            ],
            onSelected: (SortState value) {
              Get.find<PostController>().sort(value);
            },
          )
        ],
      ),
      body: _MyListView()
    );
  }
}

_MyListView

class _MyListView extends StatelessWidget {
   _MyListView({Key? key}) : super(key: key);
  final PostController controller = Get.find<PostController>();
  @override
  Widget build(BuildContext context) {
    return Obx(() => controller.isLoading.value
        ? Center(child: CircularProgressIndicator())
        : ListView.builder(
      itemCount: controller.postList.length,
      itemBuilder: (context, index) {
        PostModel item = controller.postList[index];
        return Container(
            decoration: BoxDecoration(
                borderRadius: BorderRadius.all(Radius.circular(16)),
                color: Colors.white,
                border:
                Border.all(color: Colors.blueAccent, width: 2.0)),
            margin: EdgeInsets.all(8),
            padding: EdgeInsets.all(8),
            child: RichText(
              text: TextSpan(
                style: DefaultTextStyle.of(context).style,
                children: <TextSpan>[
                  TextSpan(
                    text: item.id.toString() + ". " + item.title,
                    style: TextStyle(fontSize: 18, color: Colors.red),
                  ),
                  TextSpan(
                    text: '\n' + item.body,
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                  TextSpan(
                    text: "\nUser ID:" + item.userId.toString(),
                    style: TextStyle(fontSize: 18),
                  ),
                ],
              ),
            ));
      },
    ));
  }
}

上一篇
【第十天 - Flutter Bloc Unit Test+Mocktail 範例】
下一篇
【第十二天 - Flutter NetWork 網路判斷】
系列文
Flutter - 複製貼上到開發套件之旅30

尚未有邦友留言

立即登入留言