Created
December 15, 2025 08:06
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ## 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(); | |
| ``` |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Code đang gặp vấn đề
Nguyên nhân
Cách khắc phục
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