Tagged "Programming"

使用 ReorderableList 解決 Bloc 在 ListView 中頻繁的關閉與創建問題

最近在開發 Flutter app 時遇到了一個問題:當 ListView 中,每個 child 都使用 BlocProvider,例如:

class MyListView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocSelector<RootCubit, RootState, List<SomeObj>>(
      selector: (state) => state.items,
      builder: (_, items) {
        return ListView.builder(
          itemCount: items.length,
          itemBuilder: (_, index) {
            return BlocProvider(
              create: (_) => MyCubit(),
              child: ListTile(
                key: ValueKey(items[index]),
                title: Text(items[index].title),
              ),
            );
          },
        );
      },
    );
  }
}

如果列表的順序變更,child cubit 將會頻繁地被關閉與重新創建。此時會導致所有的 child 都不能再 emit state,否則會發生錯誤:

[ERROR: flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: Cannot emit new states after calling close
#0  BlocBase.emit (package:bloc/src/bloc_base.dart:97:9)
#1  MyCubit.fetchData.<anonymous closure> (package: myapp/features/hello/presentation/blocs/cubit.dart:203:13)
#2  GreetingRepository.fetchData(package:myapp/features/hello/infrastructure/repositories.dart:167:20)
‹asynchronous suspension>
#3  GreetingRepository.fetch.<anonymous closure (package:myapp/features/hello/infrastructure/repositories.dart:60:9)
<asynchronous suspension>

即便在每個 child 上增加了唯一的 key,也無法解決這個問題。

Read more...

解決在 Flutter 使用 Catcher 時遇到 WidgetsFlutterBinding 初始化的問題

最近引入 Catcher 來搜集 app 發生的錯誤訊息。按照 github 上的範例修改了 main function,初始化 Catcher:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  Catcher(
    debugConfig: CatcherOptions(SilentReportMode(), [
      ConsoleHandler(
        enableApplicationParameters: false,
        enableDeviceParameters: false,
        enableCustomParameters: false,
        enableStackTrace: true,
      ),
    ]),
    releaseConfig: CatcherOptions(
      SilentReportMode(),
      [
        HttpHandler(
          HttpRequestType.post,
          Uri.parse('https://x.y.z'),
          enableApplicationParameters: true,
          enableCustomParameters: false,
          enableDeviceParameters: true,
          enableStackTrace: false,
        ),
      ],
    ),
    runAppFunction: initApp,
    navigatorKey: myRootNavKey,
  );
}

Future<void> initApp() async {
  /*
    此處省略一萬個 sdk 初始化步驟 .......
  */

  runApp(const RootApp());
}

結果運行時 console 顯示錯誤:

flutter: [2023-12-29 20:47:21.680324 | Catcher | INFO] Setup localization lazily!
flutter: [2023-12-29 20:47:21.694254 | Catcher | INFO] ============================ CATCHER LOG ============================
flutter: [2023-12-29 20:47:21.694391 | Catcher | INFO] Crash occurred on 2023-12-29 20:47:21.687161
flutter: [2023-12-29 20:47:21.694430 | Catcher | INFO]
flutter: [2023-12-29 20:47:21.694508 | Catcher | INFO] ---------- ERROR ----------
flutter: [2023-12-29 20:47:21.694635 | Catcher | INFO] Zone mismatch.
The Flutter bindings were initialized in a different zone than is now being used. This will likely cause confusion and bugs as any zone-specific configuration will inconsistently use the configuration of the original binding initialization zone or this zone based on hard-to-predict factors such as which zone was active when a particular callback was set.
It is important to use the same zone when calling `ensureInitialized` on the binding as when calling `runApp` later.
To make this warning fatal, set BindingBase.debugZoneErrorsAreFatal to true before the bindings are initialized (i.e. as the first statement in `void main() { }`).
flutter: [2023-12-29 20:47:21.694679 | Catcher | INFO]
flutter: [2023-12-29 20:47:21.694899 | Catcher | INFO] ------- STACK TRACE -------
flutter: [2023-12-29 20:47:21.695005 | Catcher | INFO] #0      BindingBase.debugCheckZone.<anonymous closure> (package:flutter/src/foundation/binding.dart:503:29)
flutter: [2023-12-29 20:47:21.695041 | Catcher | INFO] #1      BindingBase.debugCheckZone (package:flutter/src/foundation/binding.dart:508:6)
flutter: [2023-12-29 20:47:21.695076 | Catcher | INFO] #2      runApp (package:flutter/src/widgets/binding.dart:1093:18)
flutter: [2023-12-29 20:47:21.695120 | Catcher | INFO] #3      initApp (package:arena/main.dart:225:3)
flutter: [2023-12-29 20:47:21.695152 | Catcher | INFO] <asynchronous suspension>
flutter: [2023-12-29 20:47:21.695183 | Catcher | INFO] #4      main.<anonymous closure>.<anonymous closure> (package:arena/main.dart:108:37)
flutter: [2023-12-29 20:47:21.695213 | Catcher | INFO] <asynchronous suspension>
flutter: [2023-12-29 20:47:21.695270 | Catcher | INFO]
flutter: [2023-12-29 20:47:21.695330 | Catcher | INFO] ======================================================================
flutter: [2023-12-29 20:47:21.695767 | Catcher | INFO] Report result: true

Read more...

在 Flutter 中使用 NestedScrollView 與 CustomScrollView

最近在修改 app 的個人頁,遇到的需求:

  1. Tab bar 切出圓角
  2. Tab bar 可以滑動,滑到 app bar 的時候固定住

用文字說不太清楚,下面直接看圖:

Imgur

頁面的 components 有:

  • App bar
  • 個人資訊區
  • Tab bar
  • Tab view

Tab view 裡是可滾動的內容, 這裡放了兩種類型:grid 與 list。 當用戶向上滑動時,app bar 固定不動,tab bar 往上滑動到 tab bar 時固定不動,下方 tab view 保持可滑動。如下圖:

Imgur

Read more...

編譯 Flutter APK

本文主要紀錄使用 flutter 編譯 app apk 時遇到的問題與解法。

版本資訊:

# Version
Flutter 3.0.1
Host OS MacOS Big Sur (11.3.1)
Android Studio 2020.3 (AI-203.7717.56.2031.7678000)
Android Platform Tool r33.0.2
OpenJDK 18.0.1.1
Android (phone) 11

Read more...

OpenIM 整合(踩坑)筆記 01

OpenIM 號稱是由一群前微信等技術專家一起出來打造的開源 IM 系統。撇開政治不談,個人覺得微信在 IM 領域確實是佼佼者,因此決定整合 OpenIM 到現有的產品中,以減少浪費時間重複造輪子。

本篇目標是串連原有的帳號系統與 OpenIM 的帳號系統,完成 自動註冊用戶到 OpenIM (下圖中的第 3 ~ 4 步驟)。

Open-IM用户注册

Read more...

在 Flutter MaterialApp 中使用指定顏色

使用 Flutter MaterialApp 時,如果直接使用 Color.fromRGBO 指定 ThemeData 的 primarySwatch 會出現類型錯誤。這是因為 primarySwatch 接收的類型是 MaterialColor 而不是 Color

Imgur

Read more...

Cross-compile for OpenWRT

這是一個實驗,在 Ubuntu 上編譯可以在 OpenWRT 上運行的程式。 參考並修改別人的方式,完成了幾個簡單的範例。

  • 編譯環境:
    • Operating System: Ubuntu 14.04 x64
    • OpenWRT version: 14.07

Read more...