Skip to content

Instantly share code, notes, and snippets.

@trantronghien
Created December 15, 2025 08:06
Show Gist options
  • Select an option

  • Save trantronghien/9717ec4c0d0fb7253b2e82dc9083ba96 to your computer and use it in GitHub Desktop.

Select an option

Save trantronghien/9717ec4c0d0fb7253b2e82dc9083ba96 to your computer and use it in GitHub Desktop.
Flutter Lỗi không lấy được size trong hàm build MaterialApp
## Code đang gặp vấn đề
```
MaterialApp(
...
builder: (context, child) {
context.read<KeyboardHeightCubit>().setKeyboardHeight(MediaQuery.of(context).viewInsets.bottom);
DeviceDimension().initValue(context); // lấy size từ context sẽ không được
});
...
main.dart
Future<void> main() async {
await runZonedGuarded(
() async {
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.light,
),
);
WidgetsFlutterBinding.ensureInitialized();
await di.init(env: ENV.STAGING.name);
runApp(const MainApp());
},
(error, stack) {
},
);
}
```
## Nguyên nhân
+ flutter run app hiện thị lên ui
```
App start
Flutter engine khởi động
View (window) attach vào OS
OS gửi metrics (size, dpr, insets)
onMetricsChanged được gọi
Frame đầu tiên được render
```
+ Tại thời điểm đọc size trước bước “OS gửi metrics” size = 0.0 ==> lúc bị lúc không nhất là chỉ bị khi build release
## Cách khắc phục
+ Cách 1: Đợi metrics non-zero
```
import 'dart:ui' as ui;
Future<ui.Size> waitForSizeDp({Duration timeout = const Duration(seconds: 2)}) async {
final binding = WidgetsFlutterBinding.ensureInitialized();
final dispatcher = binding.platformDispatcher;
ui.Size read() {
final view = dispatcher.implicitView ?? (dispatcher.views.isNotEmpty ? dispatcher.views.first : null);
if (view == null) return ui.Size.zero;
if (view.physicalSize == ui.Size.zero) return ui.Size.zero;
return view.physicalSize / view.devicePixelRatio;
}
final first = read();
if (first != ui.Size.zero) return first;
final completer = Completer<ui.Size>();
late final VoidCallback old;
old = dispatcher.onMetricsChanged ?? () {};
dispatcher.onMetricsChanged = () {
old();
final s = read();
if (s != ui.Size.zero && !completer.isCompleted) {
completer.complete(s);
}
};
try {
return await completer.future.timeout(timeout);
} finally {
// restore
dispatcher.onMetricsChanged = old;
}
}
fuc main:
...
WidgetsFlutterBinding.ensureInitialized();
final sizeDp = await waitForSizeDp(); // lấy size khi onMetricsChanged
DeviceDimension.initFromSize(sizeDp);
await di.init(env: ENV.STAGING.name);
runApp(const MainApp());
...
```
---
+ Cách 2: Giữ native splash đến khi init mọi thứ xong đây là tips/ tricks để đảm bảo có Metrics.
Vậy dùng deferFirstFrame() có giúp metrics ổn định hơn không? Gián tiếp có vì không render UI Flutter cho tới khi cho phép render. Khi frame đầu tiên render, thường metrics đã sẵn sàng
```
final binding = WidgetsFlutterBinding.ensureInitialized();
binding.deferFirstFrame();
await di.init();
runApp(const MainApp());
binding.allowFirstFrame();
```
@trantronghien
Copy link
Author

Code đang gặp vấn đề

MaterialApp(
...
 builder: (context, child) {
   context.read<KeyboardHeightCubit>().setKeyboardHeight(MediaQuery.of(context).viewInsets.bottom);
   DeviceDimension().initValue(context);  // lấy size từ context sẽ không được
});
...

main.dart
Future<void> main() async {
  await runZonedGuarded(
    () async {
      SystemChrome.setSystemUIOverlayStyle(
        const SystemUiOverlayStyle(
          statusBarColor: Colors.transparent,
          statusBarIconBrightness: Brightness.dark,
          statusBarBrightness: Brightness.light,
        ),
      );
      WidgetsFlutterBinding.ensureInitialized();
      await di.init(env: ENV.STAGING.name);
      runApp(const MainApp());
    },
    (error, stack) {
  
    },
  );
}

Nguyên nhân

  • flutter run app hiện thị lên ui
App start
↓
Flutter engine khởi động
↓
View (window) attach vào OS
↓
OS gửi metrics (size, dpr, insets) 
↓
onMetricsChanged được gọi
↓
Frame đầu tiên được render
  • Tại thời điểm đọc size trước bước “OS gửi metrics” size = 0.0 ==> lúc bị lúc không nhất là chỉ bị khi build release

Cách khắc phục

  • Cách 1: Đợi metrics non-zero
import 'dart:ui' as ui;

Future<ui.Size> waitForSizeDp({Duration timeout = const Duration(seconds: 2)}) async {
  final binding = WidgetsFlutterBinding.ensureInitialized();
  final dispatcher = binding.platformDispatcher;

  ui.Size read() {
    final view = dispatcher.implicitView ?? (dispatcher.views.isNotEmpty ? dispatcher.views.first : null);
    if (view == null) return ui.Size.zero;
    if (view.physicalSize == ui.Size.zero) return ui.Size.zero;
    return view.physicalSize / view.devicePixelRatio;
  }

  final first = read();
  if (first != ui.Size.zero) return first;

  final completer = Completer<ui.Size>();
  late final VoidCallback old;

  old = dispatcher.onMetricsChanged ?? () {};
  dispatcher.onMetricsChanged = () {
    old();
    final s = read();
    if (s != ui.Size.zero && !completer.isCompleted) {
      completer.complete(s);
    }
  };

  try {
    return await completer.future.timeout(timeout);
  } finally {
    // restore
    dispatcher.onMetricsChanged = old;
  }
}

fuc main:
...
WidgetsFlutterBinding.ensureInitialized();
final sizeDp = await waitForSizeDp(); // lấy size khi onMetricsChanged
DeviceDimension.initFromSize(sizeDp);
await di.init(env: ENV.STAGING.name);
runApp(const MainApp());
...


  • Cách 2: Giữ native splash đến khi init mọi thứ xong đây là tips/ tricks để đảm bảo có Metrics.
    Vậy dùng deferFirstFrame() có giúp metrics ổn định hơn không? Gián tiếp có vì không render UI Flutter cho tới khi cho phép render. Khi frame đầu tiên render, thường metrics đã sẵn sàng
final binding = WidgetsFlutterBinding.ensureInitialized();
binding.deferFirstFrame();

await di.init();

runApp(const MainApp());
binding.allowFirstFrame();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment