Committed by
GitHub
Merge pull request #387 from OpenFlutter/testing
Fix resizing and add rebuildFactor property.
Showing
4 changed files
with
144 additions
and
48 deletions
| @@ -16,12 +16,12 @@ class MyApp extends StatelessWidget { | @@ -16,12 +16,12 @@ class MyApp extends StatelessWidget { | ||
| 16 | // You can use the library anywhere in the app even in theme | 16 | // You can use the library anywhere in the app even in theme |
| 17 | theme: ThemeData( | 17 | theme: ThemeData( |
| 18 | primarySwatch: Colors.blue, | 18 | primarySwatch: Colors.blue, |
| 19 | - textTheme: TextTheme(bodyText2: TextStyle(fontSize: 16.sp)), | 19 | + textTheme: Typography.englishLike2018.apply(fontSizeFactor: 1.sp), |
| 20 | ), | 20 | ), |
| 21 | home: child, | 21 | home: child, |
| 22 | ); | 22 | ); |
| 23 | }, | 23 | }, |
| 24 | - child: HomePage(title: 'First Method'), | 24 | + child: const HomePage(title: 'First Method'), |
| 25 | ); | 25 | ); |
| 26 | } | 26 | } |
| 27 | } | 27 | } |
| @@ -23,6 +23,10 @@ class HomePageScaffold extends StatelessWidget { | @@ -23,6 +23,10 @@ class HomePageScaffold extends StatelessWidget { | ||
| 23 | Widget build(BuildContext context) { | 23 | Widget build(BuildContext context) { |
| 24 | printScreenInformation(); | 24 | printScreenInformation(); |
| 25 | 25 | ||
| 26 | + /// Uncomment if you wanna force current widget to be rebuilt with updated values | ||
| 27 | + /// Must use it if you use the second method, or if you use ScreenUtilInit's child. | ||
| 28 | + /// Note: don't use it along with ScreenUtil.init() | ||
| 29 | + // ScreenUtil.registerToBuild(context); | ||
| 26 | return Scaffold( | 30 | return Scaffold( |
| 27 | appBar: AppBar( | 31 | appBar: AppBar( |
| 28 | title: Text(title), | 32 | title: Text(title), |
| @@ -78,20 +82,33 @@ class HomePageScaffold extends StatelessWidget { | @@ -78,20 +82,33 @@ class HomePageScaffold extends StatelessWidget { | ||
| 78 | ), | 82 | ), |
| 79 | ), | 83 | ), |
| 80 | ), | 84 | ), |
| 85 | + Padding( | ||
| 86 | + padding: const EdgeInsets.all(18).r, | ||
| 87 | + child: Column( | ||
| 88 | + crossAxisAlignment: CrossAxisAlignment.stretch, | ||
| 89 | + children: [ | ||
| 90 | + TextField( | ||
| 91 | + decoration: InputDecoration( | ||
| 92 | + border: OutlineInputBorder(), | ||
| 93 | + ), | ||
| 94 | + ), | ||
| 95 | + 18.verticalSpace, | ||
| 81 | Text('Device width:${ScreenUtil().screenWidth}dp'), | 96 | Text('Device width:${ScreenUtil().screenWidth}dp'), |
| 82 | Text('Device height:${ScreenUtil().screenHeight}dp'), | 97 | Text('Device height:${ScreenUtil().screenHeight}dp'), |
| 83 | Text('Device pixel density:${ScreenUtil().pixelRatio}'), | 98 | Text('Device pixel density:${ScreenUtil().pixelRatio}'), |
| 84 | - Text('Bottom safe zone distance:${ScreenUtil().bottomBarHeight}dp'), | 99 | + Text( |
| 100 | + 'Bottom safe zone distance:${ScreenUtil().bottomBarHeight}dp'), | ||
| 85 | Text('Status bar height:${ScreenUtil().statusBarHeight}dp'), | 101 | Text('Status bar height:${ScreenUtil().statusBarHeight}dp'), |
| 86 | Text( | 102 | Text( |
| 87 | 'The ratio of actual width to UI design:${ScreenUtil().scaleWidth}'), | 103 | 'The ratio of actual width to UI design:${ScreenUtil().scaleWidth}'), |
| 88 | Text( | 104 | Text( |
| 89 | 'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}'), | 105 | 'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}'), |
| 90 | 10.verticalSpace, | 106 | 10.verticalSpace, |
| 91 | - Text('System font scaling factor:${ScreenUtil().textScaleFactor}'), | 107 | + Text( |
| 108 | + 'System font scaling factor:${ScreenUtil().textScaleFactor}'), | ||
| 92 | 5.verticalSpace, | 109 | 5.verticalSpace, |
| 93 | Column( | 110 | Column( |
| 94 | - crossAxisAlignment: CrossAxisAlignment.start, | 111 | + crossAxisAlignment: CrossAxisAlignment.stretch, |
| 95 | children: <Widget>[ | 112 | children: <Widget>[ |
| 96 | Text( | 113 | Text( |
| 97 | '16sp, will not change with the system.', | 114 | '16sp, will not change with the system.', |
| @@ -109,7 +126,10 @@ class HomePageScaffold extends StatelessWidget { | @@ -109,7 +126,10 @@ class HomePageScaffold extends StatelessWidget { | ||
| 109 | ), | 126 | ), |
| 110 | ), | 127 | ), |
| 111 | ], | 128 | ], |
| 112 | - ) | 129 | + ), |
| 130 | + ], | ||
| 131 | + ), | ||
| 132 | + ), | ||
| 113 | ], | 133 | ], |
| 114 | ), | 134 | ), |
| 115 | ), | 135 | ), |
| @@ -10,11 +10,11 @@ import 'package:flutter/widgets.dart'; | @@ -10,11 +10,11 @@ import 'package:flutter/widgets.dart'; | ||
| 10 | 10 | ||
| 11 | class ScreenUtil { | 11 | class ScreenUtil { |
| 12 | static const Size defaultSize = Size(360, 690); | 12 | static const Size defaultSize = Size(360, 690); |
| 13 | - static late ScreenUtil _instance; | 13 | + static ScreenUtil _instance = ScreenUtil._(); |
| 14 | 14 | ||
| 15 | /// UI设计中手机尺寸 , dp | 15 | /// UI设计中手机尺寸 , dp |
| 16 | /// Size of the phone in UI Design , dp | 16 | /// Size of the phone in UI Design , dp |
| 17 | - late Size uiSize; | 17 | + late Size _uiSize; |
| 18 | 18 | ||
| 19 | ///屏幕方向 | 19 | ///屏幕方向 |
| 20 | late Orientation _orientation; | 20 | late Orientation _orientation; |
| @@ -22,7 +22,7 @@ class ScreenUtil { | @@ -22,7 +22,7 @@ class ScreenUtil { | ||
| 22 | late double _screenWidth; | 22 | late double _screenWidth; |
| 23 | late double _screenHeight; | 23 | late double _screenHeight; |
| 24 | late bool _minTextAdapt; | 24 | late bool _minTextAdapt; |
| 25 | - BuildContext? context; | 25 | + BuildContext? _context; |
| 26 | late bool _splitScreenMode; | 26 | late bool _splitScreenMode; |
| 27 | 27 | ||
| 28 | ScreenUtil._(); | 28 | ScreenUtil._(); |
| @@ -69,16 +69,21 @@ class ScreenUtil { | @@ -69,16 +69,21 @@ class ScreenUtil { | ||
| 69 | } | 69 | } |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | + Set<Element>? _elementsToRebuild; | ||
| 73 | + | ||
| 72 | /// ### Experimental | 74 | /// ### Experimental |
| 73 | - /// Register current page and all its descendants to rebuild | 75 | + /// Register current page and all its descendants to rebuild. |
| 74 | /// Helpful when building for web and desktop | 76 | /// Helpful when building for web and desktop |
| 75 | static void registerToBuild( | 77 | static void registerToBuild( |
| 76 | BuildContext context, [ | 78 | BuildContext context, [ |
| 77 | bool withDescendants = false, | 79 | bool withDescendants = false, |
| 78 | ]) { | 80 | ]) { |
| 79 | - MediaQuery.maybeOf(context); | 81 | + final instance = ScreenUtil(); |
| 82 | + (instance._elementsToRebuild ??= {}).add(context as Element); | ||
| 83 | + | ||
| 84 | + // MediaQuery.maybeOf(context); | ||
| 80 | if (withDescendants) { | 85 | if (withDescendants) { |
| 81 | - (context as Element).visitChildren((element) { | 86 | + context.visitChildren((element) { |
| 82 | registerToBuild(element, true); | 87 | registerToBuild(element, true); |
| 83 | }); | 88 | }); |
| 84 | } | 89 | } |
| @@ -108,14 +113,16 @@ class ScreenUtil { | @@ -108,14 +113,16 @@ class ScreenUtil { | ||
| 108 | ? Orientation.landscape | 113 | ? Orientation.landscape |
| 109 | : Orientation.portrait); | 114 | : Orientation.portrait); |
| 110 | 115 | ||
| 111 | - _instance = ScreenUtil._() | ||
| 112 | - ..uiSize = designSize | 116 | + _instance |
| 117 | + .._uiSize = designSize | ||
| 113 | .._splitScreenMode = splitScreenMode | 118 | .._splitScreenMode = splitScreenMode |
| 114 | .._minTextAdapt = minTextAdapt | 119 | .._minTextAdapt = minTextAdapt |
| 115 | .._orientation = orientation | 120 | .._orientation = orientation |
| 116 | .._screenWidth = deviceSize.width | 121 | .._screenWidth = deviceSize.width |
| 117 | .._screenHeight = deviceSize.height | 122 | .._screenHeight = deviceSize.height |
| 118 | - ..context = mediaQueryContext != null ? context : null; | 123 | + .._context = context; |
| 124 | + | ||
| 125 | + _instance._elementsToRebuild?.forEach((el) => el.markNeedsBuild()); | ||
| 119 | } | 126 | } |
| 120 | 127 | ||
| 121 | ///获取屏幕方向 | 128 | ///获取屏幕方向 |
| @@ -125,41 +132,41 @@ class ScreenUtil { | @@ -125,41 +132,41 @@ class ScreenUtil { | ||
| 125 | /// 每个逻辑像素的字体像素数,字体的缩放比例 | 132 | /// 每个逻辑像素的字体像素数,字体的缩放比例 |
| 126 | /// The number of font pixels for each logical pixel. | 133 | /// The number of font pixels for each logical pixel. |
| 127 | double get textScaleFactor => | 134 | double get textScaleFactor => |
| 128 | - context != null ? MediaQuery.of(context!).textScaleFactor : 1; | 135 | + _context != null ? MediaQuery.of(_context!).textScaleFactor : 1; |
| 129 | 136 | ||
| 130 | /// 设备的像素密度 | 137 | /// 设备的像素密度 |
| 131 | /// The size of the media in logical pixels (e.g, the size of the screen). | 138 | /// The size of the media in logical pixels (e.g, the size of the screen). |
| 132 | double? get pixelRatio => | 139 | double? get pixelRatio => |
| 133 | - context != null ? MediaQuery.of(context!).devicePixelRatio : 1; | 140 | + _context != null ? MediaQuery.of(_context!).devicePixelRatio : 1; |
| 134 | 141 | ||
| 135 | /// 当前设备宽度 dp | 142 | /// 当前设备宽度 dp |
| 136 | /// The horizontal extent of this size. | 143 | /// The horizontal extent of this size. |
| 137 | double get screenWidth => | 144 | double get screenWidth => |
| 138 | - context != null ? MediaQuery.of(context!).size.width : _screenWidth; | 145 | + _context != null ? MediaQuery.of(_context!).size.width : _screenWidth; |
| 139 | 146 | ||
| 140 | ///当前设备高度 dp | 147 | ///当前设备高度 dp |
| 141 | ///The vertical extent of this size. dp | 148 | ///The vertical extent of this size. dp |
| 142 | double get screenHeight => | 149 | double get screenHeight => |
| 143 | - context != null ? MediaQuery.of(context!).size.height : _screenHeight; | 150 | + _context != null ? MediaQuery.of(_context!).size.height : _screenHeight; |
| 144 | 151 | ||
| 145 | /// 状态栏高度 dp 刘海屏会更高 | 152 | /// 状态栏高度 dp 刘海屏会更高 |
| 146 | /// The offset from the top, in dp | 153 | /// The offset from the top, in dp |
| 147 | double get statusBarHeight => | 154 | double get statusBarHeight => |
| 148 | - context == null ? 0 : MediaQuery.of(context!).padding.top; | 155 | + _context == null ? 0 : MediaQuery.of(_context!).padding.top; |
| 149 | 156 | ||
| 150 | /// 底部安全区距离 dp | 157 | /// 底部安全区距离 dp |
| 151 | /// The offset from the bottom, in dp | 158 | /// The offset from the bottom, in dp |
| 152 | double get bottomBarHeight => | 159 | double get bottomBarHeight => |
| 153 | - context == null ? 0 : MediaQuery.of(context!).padding.bottom; | 160 | + _context == null ? 0 : MediaQuery.of(_context!).padding.bottom; |
| 154 | 161 | ||
| 155 | /// 实际尺寸与UI设计的比例 | 162 | /// 实际尺寸与UI设计的比例 |
| 156 | /// The ratio of actual width to UI design | 163 | /// The ratio of actual width to UI design |
| 157 | - double get scaleWidth => screenWidth / uiSize.width; | 164 | + double get scaleWidth => screenWidth / _uiSize.width; |
| 158 | 165 | ||
| 159 | /// /// The ratio of actual height to UI design | 166 | /// /// The ratio of actual height to UI design |
| 160 | double get scaleHeight => | 167 | double get scaleHeight => |
| 161 | (_splitScreenMode ? max(screenHeight, 700) : screenHeight) / | 168 | (_splitScreenMode ? max(screenHeight, 700) : screenHeight) / |
| 162 | - uiSize.height; | 169 | + _uiSize.height; |
| 163 | 170 | ||
| 164 | double get scaleText => | 171 | double get scaleText => |
| 165 | _minTextAdapt ? min(scaleWidth, scaleHeight) : scaleWidth; | 172 | _minTextAdapt ? min(scaleWidth, scaleHeight) : scaleWidth; |
| @@ -2,15 +2,21 @@ import 'package:flutter/widgets.dart'; | @@ -2,15 +2,21 @@ import 'package:flutter/widgets.dart'; | ||
| 2 | 2 | ||
| 3 | import 'screen_util.dart'; | 3 | import 'screen_util.dart'; |
| 4 | 4 | ||
| 5 | -class ScreenUtilInit extends StatelessWidget { | 5 | +typedef RebuildFactor = bool Function(MediaQueryData old, MediaQueryData data); |
| 6 | + | ||
| 7 | +bool defaultRebuildFactor(old, data) => old.size != data.size; | ||
| 8 | + | ||
| 9 | +class ScreenUtilInit extends StatefulWidget { | ||
| 6 | /// A helper widget that initializes [ScreenUtil] | 10 | /// A helper widget that initializes [ScreenUtil] |
| 7 | const ScreenUtilInit({ | 11 | const ScreenUtilInit({ |
| 12 | + Key? key, | ||
| 8 | this.builder, | 13 | this.builder, |
| 9 | this.child, | 14 | this.child, |
| 15 | + this.rebuildFactor = defaultRebuildFactor, | ||
| 10 | this.designSize = ScreenUtil.defaultSize, | 16 | this.designSize = ScreenUtil.defaultSize, |
| 11 | this.splitScreenMode = false, | 17 | this.splitScreenMode = false, |
| 12 | this.minTextAdapt = false, | 18 | this.minTextAdapt = false, |
| 13 | - Key? key, | 19 | + this.useInheritedMediaQuery = false, |
| 14 | }) : assert( | 20 | }) : assert( |
| 15 | builder != null || child != null, | 21 | builder != null || child != null, |
| 16 | 'You must either pass builder or child or both', | 22 | 'You must either pass builder or child or both', |
| @@ -21,43 +27,106 @@ class ScreenUtilInit extends StatelessWidget { | @@ -21,43 +27,106 @@ class ScreenUtilInit extends StatelessWidget { | ||
| 21 | final Widget? child; | 27 | final Widget? child; |
| 22 | final bool splitScreenMode; | 28 | final bool splitScreenMode; |
| 23 | final bool minTextAdapt; | 29 | final bool minTextAdapt; |
| 30 | + final bool useInheritedMediaQuery; | ||
| 31 | + final RebuildFactor rebuildFactor; | ||
| 24 | 32 | ||
| 25 | /// The [Size] of the device in the design draft, in dp | 33 | /// The [Size] of the device in the design draft, in dp |
| 26 | final Size designSize; | 34 | final Size designSize; |
| 27 | 35 | ||
| 28 | @override | 36 | @override |
| 29 | - Widget build(BuildContext context) { | ||
| 30 | - bool firstFrameAllowed = false; | ||
| 31 | - final binding = WidgetsFlutterBinding.ensureInitialized(); | ||
| 32 | - binding.deferFirstFrame(); | 37 | + State<ScreenUtilInit> createState() => _ScreenUtilInitState(); |
| 38 | +} | ||
| 39 | + | ||
| 40 | +class _ScreenUtilInitState extends State<ScreenUtilInit> | ||
| 41 | + with WidgetsBindingObserver { | ||
| 42 | + late MediaQueryData mediaQueryData; | ||
| 43 | + bool wrappedInMediaQuery = false; | ||
| 44 | + | ||
| 45 | + WidgetsBinding get binding => WidgetsFlutterBinding.ensureInitialized(); | ||
| 46 | + | ||
| 47 | + MediaQueryData get newData { | ||
| 48 | + if (widget.useInheritedMediaQuery) { | ||
| 49 | + final el = context.getElementForInheritedWidgetOfExactType<MediaQuery>(); | ||
| 50 | + final mq = el?.widget as MediaQuery?; | ||
| 51 | + final data = mq?.data; | ||
| 52 | + | ||
| 53 | + if (data != null) { | ||
| 54 | + wrappedInMediaQuery = true; | ||
| 55 | + return data; | ||
| 56 | + } | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + return MediaQueryData.fromWindow(binding.window); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + Widget get child { | ||
| 63 | + return SizedBox( | ||
| 64 | + key: GlobalObjectKey( | ||
| 65 | + hashValues( | ||
| 66 | + mediaQueryData.size.width, | ||
| 67 | + mediaQueryData.size.height, | ||
| 68 | + ), | ||
| 69 | + ), | ||
| 70 | + child: widget.builder?.call(widget.child) ?? widget.child!, | ||
| 71 | + ); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + @override | ||
| 75 | + void initState() { | ||
| 76 | + super.initState(); | ||
| 77 | + mediaQueryData = newData; | ||
| 78 | + binding.addObserver(this); | ||
| 79 | + } | ||
| 33 | 80 | ||
| 34 | - final rootMediaQueryData = (context | ||
| 35 | - .getElementForInheritedWidgetOfExactType<MediaQuery>() | ||
| 36 | - ?.widget as MediaQuery?) | ||
| 37 | - ?.data; | 81 | + @override |
| 82 | + void didChangeMetrics() { | ||
| 83 | + final old = mediaQueryData; | ||
| 84 | + final data = newData; | ||
| 85 | + | ||
| 86 | + if (widget.rebuildFactor(old, data)) setState(() => mediaQueryData = data); | ||
| 87 | + } | ||
| 38 | 88 | ||
| 39 | - return LayoutBuilder( | ||
| 40 | - builder: (_, constraints) { | ||
| 41 | - if (constraints.biggest == Size.zero) return const SizedBox.shrink(); | 89 | + @override |
| 90 | + void didChangeDependencies() { | ||
| 91 | + super.didChangeDependencies(); | ||
| 92 | + didChangeMetrics(); | ||
| 93 | + } | ||
| 42 | 94 | ||
| 43 | - if (!firstFrameAllowed) { | ||
| 44 | - binding.allowFirstFrame(); | ||
| 45 | - firstFrameAllowed = true; | 95 | + @override |
| 96 | + void dispose() { | ||
| 97 | + binding.removeObserver(this); | ||
| 98 | + super.dispose(); | ||
| 46 | } | 99 | } |
| 47 | 100 | ||
| 101 | + @override | ||
| 102 | + Widget build(BuildContext _context) { | ||
| 103 | + if (mediaQueryData.size == Size.zero) return const SizedBox.shrink(); | ||
| 104 | + | ||
| 105 | + if (!wrappedInMediaQuery) { | ||
| 48 | return MediaQuery( | 106 | return MediaQuery( |
| 49 | - data: rootMediaQueryData ?? MediaQueryData.fromWindow(binding.window), | ||
| 50 | - child: Builder(builder: (_context) { | 107 | + // key: GlobalObjectKey('mediaQuery'), |
| 108 | + data: mediaQueryData, | ||
| 109 | + child: Builder( | ||
| 110 | + builder: (__context) { | ||
| 51 | ScreenUtil.init( | 111 | ScreenUtil.init( |
| 52 | - _context, | ||
| 53 | - designSize: designSize, | ||
| 54 | - splitScreenMode: splitScreenMode, | ||
| 55 | - minTextAdapt: minTextAdapt, | ||
| 56 | - ); | ||
| 57 | - return builder?.call(child) ?? child!; | ||
| 58 | - }), | 112 | + __context, |
| 113 | + designSize: widget.designSize, | ||
| 114 | + splitScreenMode: widget.splitScreenMode, | ||
| 115 | + minTextAdapt: widget.minTextAdapt, | ||
| 59 | ); | 116 | ); |
| 117 | + return child; | ||
| 60 | }, | 118 | }, |
| 119 | + ), | ||
| 61 | ); | 120 | ); |
| 62 | } | 121 | } |
| 122 | + | ||
| 123 | + ScreenUtil.init( | ||
| 124 | + _context, | ||
| 125 | + designSize: widget.designSize, | ||
| 126 | + splitScreenMode: widget.splitScreenMode, | ||
| 127 | + minTextAdapt: widget.minTextAdapt, | ||
| 128 | + ); | ||
| 129 | + | ||
| 130 | + return child; | ||
| 131 | + } | ||
| 63 | } | 132 | } |
-
Please register or login to post a comment