Mounir Bouaiche
Committed by GitHub

Fix initializing with Size(0, 0). closes #350, closes #348, closes #347 (#352)

* Fix initializing with Size(0, 0)

Only in Production Mode, Flutter starts quicker and the native platform reports the actual resolution in a certain delai. This update only wait for the real resolution.

* Upgrade to latest gradle version and enable Jetifier and use AndroidX

* Update example flutter version

* Optimize BugFix and some boilerplate removal

* Update example app

* Fix creating adaptive radius using extension

* Update example/ios/.gitignore

* Update README.md

* Update screenutil_init.dart
# flutter_screenutil
[![pub package](https://img.shields.io/pub/v/flutter_screenutil.svg)](https://pub.dev/packages/flutter_screenutil)
[![pub points](https://badges.bar/flutter_screenutil/pub%20points)](https://pub.dev/packages/flutter_screenutil/score)
[![popularity](https://badges.bar/flutter_screenutil/popularity)](https://pub.dev/packages/flutter_screenutil/score)
... ... @@ -15,11 +16,13 @@
[Update log](https://github.com/OpenFlutter/flutter_screenutil/blob/master/CHANGELOG.md)
## Usage:
## Usage
### Add dependency
### Add dependency:
Please check the latest version before installation.
If there is any problem with the new version, please use the previous version
```yaml
dependencies:
flutter:
... ... @@ -27,61 +30,31 @@ dependencies:
# add flutter_screenutil
flutter_screenutil: ^{latest version}
```
### Add the following imports to your Dart code:
### Add the following imports to your Dart code
```dart
import 'package:flutter_screenutil/flutter_screenutil.dart';
```
### Property
<table style="width:100%">
<tr>
<th>Property</th>
<th>Type</th>
<th>Default Value</th>
<th>Description</th>
</tr>
<tr>
<td>designSize</td>
<td>Size</td>
<td>Size(360, 690)</td>
<td>The size of the device screen in the design draft, in dp</td>
</tr>
<tr>
<td>builder</td>
<td>Widget Function()</td>
<td>Container()</td>
<td>Generally returning a Function of MaterialApp type</td>
</tr>
<tr>
<td>orientation</td>
<td>Orientation</td>
<td>portrait</td>
<td>screen orientation</td>
</tr>
<tr>
<td>splitScreenMode</td>
<td>bool</td>
<td>true</td>
<td>support for split screen</td>
</tr>
<tr>
<td>minTextAdapt</td>
<td>bool</td>
<td>false</td>
<td>Whether to adapt the text according to the minimum of width and height</td>
</tr>
<tr>
<td>context</td>
<td>BuildContext</td>
<td>null</td>
<td>If context!=null, screen changes will be more sensitive</td>
</tr>
</table>
### Initialize and set the fit size and font size to scale according to the system's "font size" accessibility option
| Property | Type | Default Value | Description |
| --------------- | ------------- | -------------- | ---------------------------------------------------------------------- |
| deviceSize | Size | null | The size of the physical device |
| designSize | Size | Size(360, 690) | The size of the device screen in the design draft, in dp |
| builder | WidgetBuilder | (*required*) | Generally returning a Function of MaterialApp type |
| orientation | Orientation | portrait | screen orientation |
| splitScreenMode | bool | true | support for split screen |
| minTextAdapt | bool | false | Whether to adapt the text according to the minimum of width and height |
| context | BuildContext | null | Get physical device data if not provided, by MediaQuery.of(context) |
### Initialize and set the fit size and font size to scale according to the system's "font size" accessibility option |
Please set the size of the design draft before use, the width and height of the design draft.
#### The first way:
#### The first way
```dart
void main() => runApp(MyApp());
... ... @@ -93,44 +66,66 @@ class MyApp extends StatelessWidget {
designSize: Size(360, 690),
minTextAdapt: true,
splitScreenMode: true,
builder: () =>
MaterialApp(
//... other code
builder: (context, widget) {
//add this line
ScreenUtil.setContext(context);
return MediaQuery(
//Setting font does not change with system font size
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: widget!,
);
},
theme: ThemeData(
textTheme: TextTheme(
//To support the following, you need to use the first initialization method
button: TextStyle(fontSize: 45.sp)
),
),
builder: (_) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// Use this line to prevent extra rebuilds
useInheritedMediaQuery: true,
title: 'First Method',
// You can use the library anywhere in the app even in theme
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)),
),
home: HomePage(title: 'First Method'),
);
},
);
}
}
```
#### The second way:Does not support font adaptation in the textTheme of MaterialApp's theme.
#### The second way:You need a trick to support font adaptation in the textTheme of app theme
**Hybrid development uses the second way**
**(If it is not necessary, it is recommended to use the second)**
not support this:
```dart
MaterialApp(
...
//To support the following, you need to use the first initialization method
theme: ThemeData(
textTheme: TextTheme(
//To support the following, you need to use the first initialization method
button: TextStyle(fontSize: 45.sp)
),
),
textTheme: TextTheme(
button: TextStyle(fontSize: 45.sp)
),
),
)
```
but you can do this:
```dart
void main() async {
// Add this line
await ScreenUtil.ensureScreenSize();
runApp(MyApp());
}
...
MaterialApp(
...
builder: (ctx, child) {
ScreenUtil.init(ctx);
return Theme(
data: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)),
),
child: HomePage(title: 'FlutterScreenUtil Demo'),
);
},
)
```
... ... @@ -161,22 +156,14 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
//Set the fit size (fill in the screen size of the device in the design) If the design is based on the size of the 360*690(dp)
ScreenUtil.init(
BoxConstraints(
maxWidth: MediaQuery.of(context).size.width,
maxHeight: MediaQuery.of(context).size.height),
designSize: Size(360, 690),
context: context,
minTextAdapt: true,
orientation: Orientation.portrait);
return Scaffold();
//Set the fit size (fill in the screen size of the device in the design)
//If the design is based on the size of the 360*690(dp)
ScreenUtil.init(context, designSize: const Size(360, 690));
...
}
}
```
### Use:
### API
#### Pass the dp size of the design draft
... ... @@ -208,7 +195,7 @@ class _HomePageState extends State<HomePage> {
EdgeInsets.only(left:8,right:8).r // EdgeInsets.only(left:8.r,right:8.r).
```
#### Adapt screen size
#### Adapt screen size
Pass the dp size of the design draft((The unit is the same as the unit at initialization)):
... ... @@ -221,20 +208,24 @@ If your dart sdk>=2.6, you can use extension functions:
example:
instead of :
```dart
Container(
width: ScreenUtil().setWidth(50),
height:ScreenUtil().setHeight(200),
width: ScreenUtil().setWidth(50),
height:ScreenUtil().setHeight(200),
)
```
you can use it like this:
```dart
Container(
width: 50.w,
height:200.h
width: 50.w,
height:200.h
)
```
**Note**
#### `Note`
The height can also use setWidth to ensure that it is not deformed(when you want a square)
... ... @@ -245,29 +236,33 @@ Generally speaking, 50.w!=50.h.
```dart
//for example:
///If you want to display a square:
///The UI may show a rectangle:
//If you want to display a rectangle:
Container(
width: 375.w,
height: 375.h,
),
width: 375.w,
height: 375.h,
),
////If you want to display a square:
//If you want to display a square based on width:
Container(
width: 300.w,
height: 300.w,
),
width: 300.w,
height: 300.w,
),
or
//If you want to display a square based on height:
Container(
width: 300.h,
height: 300.h,
),
//If you want to display a square based on minimum(height, width):
Container(
width: 300.r,
height: 300.r,
),
width: 300.r,
height: 300.r,
),
```
#### Adapter font
#### Adapter font:
``` dart
//Incoming font size(The unit is the same as the unit at initialization)
ScreenUtil().setSp(28)
... ... @@ -275,60 +270,74 @@ ScreenUtil().setSp(28)
//for example:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'16sp, will not change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
textScaleFactor: 1.0,
),
Text(
'16sp,if data is not set in MediaQuery,my font size will change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
),
],
)
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'16sp, will not change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
textScaleFactor: 1.0,
),
Text(
'16sp,if data is not set in MediaQuery,my font size will change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
),
],
)
```
#### Setting font does not change with system font size
APP global:
```dart
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter_ScreenUtil',
theme: ThemeData(
primarySwatch: Colors.blue,
),
builder: (context, widget) {
return MediaQuery(
///Setting font does not change with system font size
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: widget,
);
},
home: HomePage(title: 'FlutterScreenUtil Demo'),
),
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter_ScreenUtil',
theme: ThemeData(
primarySwatch: Colors.blue,
),
builder: (context, widget) {
return MediaQuery(
///Setting font does not change with system font size
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: widget,
);
},
home: HomePage(title: 'FlutterScreenUtil Demo'),
),
```
Separate Text:
Specified Text:
```dart
Text("text", textScaleFactor: 1.0)
```
Specified Widget:
```dart
MediaQuery(
// If there is no context available you can wrap [MediaQuery] with [Builder]
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: AnyWidget(),
)
```
[widget test](https://github.com/OpenFlutter/flutter_screenutil/issues/115)
### Example:
### Example
[example demo](https://github.com/OpenFlutter/flutter_screenutil/blob/master/example/lib)
To use second method run: `flutter run --dart-define=method=2`
[example demo](https://github.com/OpenFlutter/flutter_screenutil/blob/master/example/lib/main.dart)
### Effect:
### Effect
![effect](demo_en.png)
![tablet effect](demo_tablet_en.png)
... ...
... ... @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
compileSdkVersion 31
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
... ... @@ -40,7 +40,7 @@ android {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "li.zhuoyuan.example"
minSdkVersion 16
targetSdkVersion 28
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
... ...
... ... @@ -12,6 +12,7 @@
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:exported="true"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
... ... @@ -23,14 +24,6 @@
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
... ...
... ... @@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
... ...
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
... ...
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
... ...
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
!default.pbxuser
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/app.flx
/Flutter/app.zip
/Flutter/flutter_assets/
/Flutter/App.framework
/Flutter/Flutter.framework
/Flutter/Generated.xcconfig
/ServiceDefinitions.json
Pods/
.symlinks/
... ...
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter/widgets.dart';
import 'src/first_method.dart' as firstMethod;
import 'src/second_method.dart' as secondMethod;
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//Set the fit size (fill in the screen size of the device in the design) If the design is based on the size of the iPhone6 ​​(iPhone6 ​​750*1334)
return ScreenUtilInit(
designSize: Size(360, 690),
minTextAdapt: true,
splitScreenMode: true,
builder: () => MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter_ScreenUtil',
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(button: TextStyle(fontSize: 45.sp)),
),
builder: (context, widget) {
ScreenUtil.setContext(context);
return MediaQuery(
//Setting font does not change with system font size
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: widget,
);
},
home: HomePage(title: 'FlutterScreenUtil Demo'),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
printScreenInformation();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
// Using Extensions
Container(
padding: EdgeInsets.all(10.w),
width: 0.5.sw,
height: 200.h,
color: Colors.red,
child: Text(
'My actual width: ${0.5.sw}dp \n\n'
'My actual height: ${200.h}dp',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
),
// Without using Extensions
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: ScreenUtil().setWidth(180),
height: ScreenUtil().setHeight(200),
color: Colors.blue,
child: Text(
'My design draft width: 180dp\n\n'
'My design draft height: 200dp',
style: TextStyle(
color: Colors.white,
fontSize: ScreenUtil().setSp(12),
),
),
),
],
),
Container(
padding: EdgeInsets.all(10.w),
width: 100.r,
height: 100.r,
color: Colors.green,
child: Text(
'I am a square with a side length of 100',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
),
Text('Device width:${ScreenUtil().screenWidth}dp'),
Text('Device height:${ScreenUtil().screenHeight}dp'),
Text('Device pixel density:${ScreenUtil().pixelRatio}'),
Text('Bottom safe zone distance:${ScreenUtil().bottomBarHeight}dp'),
Text('Status bar height:${ScreenUtil().statusBarHeight}dp'),
Text(
'The ratio of actual width to UI design:${ScreenUtil().scaleWidth}',
textAlign: TextAlign.center,
),
Text(
'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}',
textAlign: TextAlign.center,
),
SizedBox(
height: 10.h,
),
Text('System font scaling factor:${ScreenUtil().textScaleFactor}'),
SizedBox(height: 5),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'16sp, will not change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
textScaleFactor: 1.0,
),
Text(
'16sp,if data is not set in MediaQuery,my font size will change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
),
],
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
SystemChrome.setPreferredOrientations([
MediaQuery.of(context).orientation == Orientation.portrait
? DeviceOrientation.landscapeRight
: DeviceOrientation.portraitUp,
]);
// setState(() {});
},
child: Icon(Icons.screen_rotation),
),
);
}
void printScreenInformation() {
print('Device Size:${Size(1.sw, 1.sh)}');
print('Device pixel density:${ScreenUtil().pixelRatio}');
print('Bottom safe zone distance dp:${ScreenUtil().bottomBarHeight}dp');
print('Status bar height dp:${ScreenUtil().statusBarHeight}dp');
print('The ratio of actual width to UI design:${ScreenUtil().scaleWidth}');
print(
'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}');
print('System font scaling:${ScreenUtil().textScaleFactor}');
print('0.5 times the screen width:${0.5.sw}dp');
print('0.5 times the screen height:${0.5.sh}dp');
print('Screen orientation:${ScreenUtil().orientation}');
}
const method = int.fromEnvironment('method', defaultValue: 1);
runApp(method == 1 ? firstMethod.MyApp() : secondMethod.MyApp());
}
... ...
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter/widgets.dart';
import 'src_zh/first_method.dart' as firstMethod;
import 'src_zh/second_method.dart' as secondMethod;
void main() => runApp(MyApp());
void main() {
const method = int.fromEnvironment('method', defaultValue: 1);
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter_ScreenUtil',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(title: 'FlutterScreenUtil Demo'),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
//Set the fit size (fill in the screen size of the device in the design) If the design is based on the size of the 360*690
ScreenUtil.init(
BoxConstraints(
maxWidth: MediaQuery.of(context).size.width,
maxHeight: MediaQuery.of(context).size.height),
designSize: Size(360, 690),
context: context,
);
printScreenInformation();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: 180.w,
height: 200.h,
color: Colors.red,
child: Text(
'我的实际宽度:${180.w}dp \n'
'我的实际高度:${200.h}dp',
style: TextStyle(color: Colors.white, fontSize: 12.sp),
),
),
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: ScreenUtil().setWidth(180),
height: ScreenUtil().setHeight(200),
color: Colors.blue,
child: Text(
'我的设计稿宽度: 180dp \n'
'我的设计稿高度: 200dp',
style: TextStyle(
color: Colors.white,
fontSize: ScreenUtil().setSp(12))),
),
],
),
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: 100.r,
height: 100.r,
color: Colors.green,
child: Text(
'我是正方形,边长是100',
style: TextStyle(
color: Colors.white,
fontSize: ScreenUtil().setSp(12),
),
),
),
Text('设备宽度:${ScreenUtil().screenWidth}dp'),
Text('设备高度:${ScreenUtil().screenHeight}dp'),
Text('设备的像素密度:${ScreenUtil().pixelRatio}'),
Text('底部安全区距离:${ScreenUtil().bottomBarHeight}dp'),
Text('状态栏高度:${ScreenUtil().statusBarHeight}dp'),
Text(
'实际宽度与设计稿的比例:${ScreenUtil().scaleWidth}',
textAlign: TextAlign.center,
),
Text(
'实际高度与设计稿的比例:${ScreenUtil().scaleHeight}',
textAlign: TextAlign.center,
),
SizedBox(
height: 50.h,
),
Text('系统的字体缩放比例:${ScreenUtil().textScaleFactor}'),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'我的文字大小在设计稿上是16dp,因为设置了`textScaleFactor`,所以不会随着系统的文字缩放比例变化',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
textScaleFactor: 1.0,
),
Text(
'我的文字大小在设计稿上是16dp,会随着系统的文字缩放比例变化',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
),
],
)
],
),
),
);
}
void printScreenInformation() {
print('设备宽度:${1.sw}dp');
print('设备高度:${1.sh}dp');
print('设备的像素密度:${ScreenUtil().pixelRatio}');
print('底部安全区距离:${ScreenUtil().bottomBarHeight}dp');
print('状态栏高度:${ScreenUtil().statusBarHeight}dp');
print('实际宽度和字体(dp)与设计稿(dp)的比例:${ScreenUtil().scaleWidth}');
print('实际高度(dp)与设计稿(dp)的比例:${ScreenUtil().scaleHeight}');
print('高度相对于设计稿放大的比例:${ScreenUtil().scaleHeight}');
print('系统的字体缩放比例:${ScreenUtil().textScaleFactor}');
print('屏幕宽度的0.5:${0.5.sw}dp');
print('屏幕高度的0.5:${0.5.sh}dp');
print('屏幕方向:${ScreenUtil().orientation}');
}
runApp(method == 1 ? firstMethod.MyApp() : secondMethod.MyApp());
}
... ...
import 'package:example/src/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it
return ScreenUtilInit(
builder: (context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// Use this line to prevent extra rebuilds
useInheritedMediaQuery: true,
title: 'First Method',
// You can use the library anywhere in the app even in theme
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)),
),
home: HomePage(title: 'First Method'),
);
},
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) => HomePageScaffold(title: widget.title);
}
... ...
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HomePageScaffold extends StatelessWidget {
const HomePageScaffold({Key? key, required this.title}) : super(key: key);
void printScreenInformation() {
print('Device Size:${Size(1.sw, 1.sh)}');
print('Device pixel density:${ScreenUtil().pixelRatio}');
print('Bottom safe zone distance dp:${ScreenUtil().bottomBarHeight}dp');
print('Status bar height dp:${ScreenUtil().statusBarHeight}dp');
print('The ratio of actual width to UI design:${ScreenUtil().scaleWidth}');
print(
'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}');
print('System font scaling:${ScreenUtil().textScaleFactor}');
print('0.5 times the screen width:${0.5.sw}dp');
print('0.5 times the screen height:${0.5.sh}dp');
print('Screen orientation:${ScreenUtil().orientation}');
}
@override
Widget build(BuildContext context) {
printScreenInformation();
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
// Using Extensions
Container(
padding: EdgeInsets.all(10.w),
width: 0.5.sw,
height: 200.h,
color: Colors.red,
child: Text(
'My actual width: ${0.5.sw}dp \n\n'
'My actual height: ${200.h}dp',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
),
// Without using Extensions
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: ScreenUtil().setWidth(180),
height: ScreenUtil().setHeight(200),
color: Colors.blue,
child: Text(
'My design draft width: 180dp\n\n'
'My design draft height: 200dp',
style: TextStyle(
color: Colors.white,
fontSize: ScreenUtil().setSp(12),
),
),
),
],
),
Container(
padding: EdgeInsets.all(10.w),
width: 100.r,
height: 100.r,
color: Colors.green,
child: Text(
'I am a square with a side length of 100',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
),
),
),
Text('Device width:${ScreenUtil().screenWidth}dp'),
Text('Device height:${ScreenUtil().screenHeight}dp'),
Text('Device pixel density:${ScreenUtil().pixelRatio}'),
Text('Bottom safe zone distance:${ScreenUtil().bottomBarHeight}dp'),
Text('Status bar height:${ScreenUtil().statusBarHeight}dp'),
Text(
'The ratio of actual width to UI design:${ScreenUtil().scaleWidth}',
textAlign: TextAlign.center,
),
Text(
'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}',
textAlign: TextAlign.center,
),
SizedBox(
height: 10.h,
),
Text('System font scaling factor:${ScreenUtil().textScaleFactor}'),
SizedBox(height: 5),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'16sp, will not change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
textScaleFactor: 1.0,
),
Text(
'16sp,if data is not set in MediaQuery,my font size will change with the system.',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
),
],
)
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
SystemChrome.setPreferredOrientations([
MediaQuery.of(context).orientation == Orientation.portrait
? DeviceOrientation.landscapeRight
: DeviceOrientation.portraitUp,
]);
// setState(() {});
},
label: const Text('Rotate'),
),
);
}
final String title;
}
... ...
import 'package:example/src/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// Note that you still can use [Theme] to theme your widget, but if you want
/// to theme MaterialApp you must use ScreenUtil.init in builder method and
/// wrap child with Theme, and remove theme and home properties from MaterialApp.
/// See [MyThemedApp].
///
/// example
/// ```dart
/// Theme(
/// data: ThemeData(...),
/// child: widget,
/// )
/// ```
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Second Method',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(title: 'Second Method'),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return HomePageScaffold(title: widget.title);
}
}
class MyThemedApp extends StatelessWidget {
const MyThemedApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'First Method (Themed)',
builder: (ctx, child) {
return Theme(
data: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)),
),
child: HomePage(title: 'FlutterScreenUtil Demo'),
);
},
);
}
}
... ...
import 'package:example/src_zh/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it
return ScreenUtilInit(
builder: (context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: '第一种方法',
// You can use the library anywhere in the app even in theme
theme: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)),
),
home: HomePage(title: '第一种方法'),
);
},
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) => HomePageScaffold(title: widget.title);
}
... ...
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HomePageScaffold extends StatelessWidget {
const HomePageScaffold({Key? key, required this.title}) : super(key: key);
void printScreenInformation() {
print('设备宽度:${1.sw}dp');
print('设备高度:${1.sh}dp');
print('设备的像素密度:${ScreenUtil().pixelRatio}');
print('底部安全区距离:${ScreenUtil().bottomBarHeight}dp');
print('状态栏高度:${ScreenUtil().statusBarHeight}dp');
print('实际宽度和字体(dp)与设计稿(dp)的比例:${ScreenUtil().scaleWidth}');
print('实际高度(dp)与设计稿(dp)的比例:${ScreenUtil().scaleHeight}');
print('高度相对于设计稿放大的比例:${ScreenUtil().scaleHeight}');
print('系统的字体缩放比例:${ScreenUtil().textScaleFactor}');
print('屏幕宽度的0.5:${0.5.sw}dp');
print('屏幕高度的0.5:${0.5.sh}dp');
print('屏幕方向:${ScreenUtil().orientation}');
}
@override
Widget build(BuildContext context) {
printScreenInformation();
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: 180.w,
height: 200.h,
color: Colors.red,
child: Text(
'我的实际宽度:${180.w}dp \n'
'我的实际高度:${200.h}dp',
style: TextStyle(color: Colors.white, fontSize: 12.sp),
),
),
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: ScreenUtil().setWidth(180),
height: ScreenUtil().setHeight(200),
color: Colors.blue,
child: Text(
'我的设计稿宽度: 180dp \n'
'我的设计稿高度: 200dp',
style: TextStyle(
color: Colors.white,
fontSize: ScreenUtil().setSp(12))),
),
],
),
Container(
padding: EdgeInsets.all(ScreenUtil().setWidth(10)),
width: 100.r,
height: 100.r,
color: Colors.green,
child: Text(
'我是正方形,边长是100',
style: TextStyle(
color: Colors.white,
fontSize: ScreenUtil().setSp(12),
),
),
),
Text('设备宽度:${ScreenUtil().screenWidth}dp'),
Text('设备高度:${ScreenUtil().screenHeight}dp'),
Text('设备的像素密度:${ScreenUtil().pixelRatio}'),
Text('底部安全区距离:${ScreenUtil().bottomBarHeight}dp'),
Text('状态栏高度:${ScreenUtil().statusBarHeight}dp'),
Text(
'实际宽度与设计稿的比例:${ScreenUtil().scaleWidth}',
textAlign: TextAlign.center,
),
Text(
'实际高度与设计稿的比例:${ScreenUtil().scaleHeight}',
textAlign: TextAlign.center,
),
SizedBox(
height: 50.h,
),
Text('系统的字体缩放比例:${ScreenUtil().textScaleFactor}'),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'我的文字大小在设计稿上是16dp,因为设置了`textScaleFactor`,所以不会随着系统的文字缩放比例变化',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
textScaleFactor: 1.0,
),
Text(
'我的文字大小在设计稿上是16dp,会随着系统的文字缩放比例变化',
style: TextStyle(
color: Colors.black,
fontSize: 16.sp,
),
),
],
)
],
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
SystemChrome.setPreferredOrientations([
MediaQuery.of(context).orientation == Orientation.portrait
? DeviceOrientation.landscapeRight
: DeviceOrientation.portraitUp,
]);
// setState(() {});
},
label: const Text('旋转'),
),
);
}
final String title;
}
... ...
import 'package:example/src_zh/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
/// 请注意,您仍然可以使用 [Theme] 为您的小部件设置主题,但如果您想为 MaterialApp
/// 设置主题,您必须在 builder 方法中使用 ScreenUtil.init 并使用 Theme 包装子项
/// 并从 MaterialApp 中删除主题和主页属性。 请参阅 [MyThemedApp]。
///
/// 例子
/// ```dart
/// Theme(
/// data: ThemeData(...),
/// child: widget,
/// )
/// ```
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it
return MaterialApp(
debugShowCheckedModeBanner: false,
title: '第二种方法',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(title: '第二种方法'),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
ScreenUtil.init(context);
return HomePageScaffold(title: widget.title);
}
}
class MyThemedApp extends StatelessWidget {
const MyThemedApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: '第二种方法(带主题)',
builder: (ctx, child) {
return Theme(
data: ThemeData(
primarySwatch: Colors.blue,
textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)),
),
child: HomePage(title: '第二种方法(带主题)'),
);
},
);
}
}
... ...
name: example
description: flutter_screenutil的使用示例
publish_to: none
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
... ... @@ -10,20 +11,16 @@ description: flutter_screenutil的使用示例
version: 1.0.0+1
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
sdk: ">=2.12.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
flutter_screenutil:
path: ../
dev_dependencies:
flutter_test:
sdk: flutter
test: ^1.15.7
dependency_overrides:
flutter_screenutil:
path: ../
... ...
... ... @@ -8,7 +8,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:example/main.dart';
import 'package:example/src/first_method.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
... ...
... ... @@ -3,9 +3,10 @@
* email: zhuoyuan93@gmail.com
*/
import 'dart:math';
import 'dart:math' show min, max;
import 'dart:ui' show FlutterWindow;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class ScreenUtil {
static const Size defaultSize = Size(360, 690);
... ... @@ -21,7 +22,7 @@ class ScreenUtil {
late double _screenWidth;
late double _screenHeight;
late bool _minTextAdapt;
late BuildContext? context;
BuildContext? context;
late bool _splitScreenMode;
ScreenUtil._();
... ... @@ -30,26 +31,74 @@ class ScreenUtil {
return _instance;
}
/// Manually wait for window size to be initialized
///
/// `Recommended` to use before you need access window size
/// or in custom splash/bootstrap screen [FutureBuilder]
///
/// example:
/// ```dart
/// ...
/// ScreenUtil.init(context, ...);
/// ...
/// FutureBuilder(
/// future: Future.wait([..., ensureScreenSize(), ...]),
/// builder: (context, snapshot) {
/// if (snapshot.hasData) return const HomeScreen();
/// return Material(
/// child: LayoutBuilder(
/// ...
/// ),
/// );
/// },
/// )
/// ```
static Future<void> ensureScreenSize([
FlutterWindow? window,
Duration duration = const Duration(milliseconds: 10),
]) async {
final binding = WidgetsFlutterBinding.ensureInitialized();
window ??= binding.window;
if (window.viewConfiguration.geometry.isEmpty) {
return Future.delayed(duration, () async {
binding.deferFirstFrame();
await ensureScreenSize(window, duration);
return binding.allowFirstFrame();
});
}
}
static void setContext(BuildContext context) {
_instance.context = context;
}
/// Initializing the library.
static void init(
BoxConstraints constraints, {
BuildContext? context,
Orientation orientation = Orientation.portrait,
BuildContext context, {
Orientation? orientation,
Size? deviceSize,
Size designSize = defaultSize,
bool splitScreenMode = false,
bool minTextAdapt = false,
}) {
print('init called');
final deviceData = MediaQuery.maybeOf(context).nonEmptySizeOrNull();
deviceSize ??= deviceData?.size ?? designSize;
orientation ??= deviceData?.orientation ??
(deviceSize.width > deviceSize.height
? Orientation.landscape
: Orientation.portrait);
_instance = ScreenUtil._()
..uiSize = designSize
.._splitScreenMode = splitScreenMode
.._minTextAdapt = minTextAdapt
.._orientation = orientation
.._screenWidth = constraints.maxWidth
.._screenHeight = constraints.maxHeight;
if (context != null) setContext(context);
.._screenWidth = deviceSize.width
.._screenHeight = deviceSize.height
..context = deviceData != null ? context : null;
}
///获取屏幕方向
... ... @@ -138,3 +187,12 @@ class ScreenUtil {
Widget setVerticalSpacingRadius(num height) =>
SizedBox(height: radius(height));
}
extension on MediaQueryData? {
MediaQueryData? nonEmptySizeOrNull() {
if (this?.size.isEmpty ?? true)
return null;
else
return this;
}
}
... ...
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
import 'screen_util.dart';
... ... @@ -12,7 +13,7 @@ class ScreenUtilInit extends StatelessWidget {
Key? key,
}) : super(key: key);
final Widget Function() builder;
final WidgetBuilder builder;
final bool splitScreenMode;
final bool minTextAdapt;
... ... @@ -20,24 +21,26 @@ class ScreenUtilInit extends StatelessWidget {
final Size designSize;
@override
Widget build(BuildContext context) {
return MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window),
child: LayoutBuilder(builder: (_, BoxConstraints constraints) {
if (constraints.maxWidth != 0) {
final Orientation orientation =
constraints.maxWidth > constraints.maxHeight
? Orientation.landscape
: Orientation.portrait;
ScreenUtil.init(constraints,
context: _,
orientation: orientation,
designSize: designSize,
splitScreenMode: splitScreenMode,
minTextAdapt: minTextAdapt);
return builder();
Widget build(BuildContext _) {
bool firstFrameAllowed = false;
RendererBinding.instance!.deferFirstFrame();
return MediaQuery.fromWindow(
child: Builder(builder: (context) {
if (MediaQuery.of(context).size == Size.zero) return const SizedBox();
ScreenUtil.init(
context,
designSize: designSize,
splitScreenMode: splitScreenMode,
minTextAdapt: minTextAdapt,
);
if (!firstFrameAllowed) {
RendererBinding.instance!.allowFirstFrame();
firstFrameAllowed = true;
}
return Container();
return builder(context);
}),
);
}
... ...
... ... @@ -71,9 +71,7 @@ extension BorderRaduisExtension on BorderRadius {
extension RaduisExtension on Radius {
/// Creates adapt Radius using r [SizeExtension].
Radius get r => this
..x.r
..y.r;
Radius get r => Radius.elliptical(x.r, y.r);
}
extension BoxConstraintsExtension on BoxConstraints {
... ...
name: flutter_screenutil
description: A flutter plugin for adapting screen and font size.Guaranteed to look good on different models
version: 5.3.0
version: 5.3.2
homepage: https://github.com/OpenFlutter/flutter_screenutil
publish_to: https://pub.dev
... ...