Mounir Bouaiche
Committed by GitHub

Major updates & fixes (#491)

* Clean codes & add features & fix bugs

Features/Bugs:
- Feature: You can select what classes needs to be rebuilt instead of rebuilding everything, if you have widget A, either addi SU mixin or add 'A' to list ScreenUtilInit.responsiveWidgets
- Feature: Using ScreenUtilInit.builder is optional (use it only when using library in theme)
- Bug: Second call to ScreenUtil.init ignores any existing values and uses the default values when not provided, use ScreenUtil.configure instead
- Bug: ScreenUtil.ensureScreenSize raises an overflow error

* Update version

* Add List of flutter widgets

* Update logic if allowing widget to being rebuilt

* Little code solidity

* Add scale factors: diagonal & diameter

* Add option for how font size should be scaled

* Update support to Dart >= 2.17.0

* Add fontSizeResolver to init + helpers

* Add ensureScreenSize to ScreenUtilInit constructor

* Fix ensureScreenSize on web

* Update Runner.rc

* Add some methods to extensions

* Update widget_test

* Remove extra deps

* Clean code

* Add ensureScreenSizeAndInit + make init sync

* Update README.md

* Update CHANGELOG.md

* Update version to 5.9.0

* Rename version

* Adding tests

* Changing version in CHANGELOG.md

---------

Co-authored-by: Mounir Bouaiche <mounir-b@dba.ma>
... ... @@ -139,3 +139,6 @@ doc
.lock
coverage*
*.lock
# Don't commit .fvm directory containing machine-specific symlink to sdk & flutter version
**/.fvm
... ...
# 5.9.0-beta
- ScreenUtilInit won't rebuild the whole widget tree
- Add `fontSizeResolver` to specify how font size should be scaled
- Add `diameter` & `diagonal` factors
- `useInheritedMediaQuery` has not effect, and will be removed in next release
- Fix `ensureScreenSize` in web platform
# 5.8.4
- bug fix
- change useInheritedMediaQuery default value to false
... ...
... ... @@ -38,28 +38,36 @@ dependencies:
import 'package:flutter_screenutil/flutter_screenutil.dart';
```
### Property
### Properties
| 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 | Function | null | Return widget that uses the library in a property (ex: MaterialApp's theme) |
| child | Widget | null | A part of builder that its dependencies/properties don't use the library |
| rebuildFactor | Function | *default* | Returns whether to rebuild or not when screen metrics changes. |
| orientation | Orientation | portrait | screen orientation |
| rebuildFactor | Function | *default* | Function that take old and new screen metrics and returns whether to rebuild or not when changes. |
| splitScreenMode | bool | false | 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) |
| useInheritedMediaQuery | bool | false | Recommended use `false` avoid rebuild very frequently <br/><br/> ~~Set this to true for Flutter 3.10 to avoid keyboard overlay on TextField~~ |
| fontSizeResolver | Function | *default* | Function that specify how font size should be adapted. Default is that font size scale with width of screen. |
| reponsiveWidgets | Iterable<String> | null | List/Set of widget names that should be included in rebuilding tree. (See [How flutter_screenutil marks a widget needs build](#rebuild-list)) |
**Note : You must either provide builder, child or both.**
### Rebuild list
Starting from version 5.9.0, ScreenUtilInit won't rebuild the whole widget tree, instead it will mark widget needs build only if:
- Widget is not a flutter widget (widgets are available in [Flutter Docs](https://docs.flutter.dev/reference/widgets))
- Widget does not start with underscore (`_`)
- Widget does not declare `SU` mixin
- `reponsiveWidgets` does not contains widget name
If you have a widget that uses the library and doesn't meet these options you can either add `SU` mixin or add widget name in responsiveWidgets list.
### 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 (You must use it once in your app)
#### The first way (You should use it once in your app)
```dart
void main() => runApp(MyApp());
... ... @@ -74,7 +82,8 @@ class MyApp extends StatelessWidget {
designSize: const Size(360, 690),
minTextAdapt: true,
splitScreenMode: true,
builder: (context , child) {
// Use builder only if you need to use library outside ScreenUtilInit context
builder: (_ , child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'First Method',
... ... @@ -169,6 +178,8 @@ class _HomePageState extends State<HomePage> {
}
```
**Note: calling ScreenUtil.init second time, any non-provided parameter will not be replaced with default value. Use ScreenUtil.configure instead**
### API
#### Pass the dp size of the design draft
... ...
/////////////////////////////////////////////////////////////////////////
/// Generated via plugin: flutter_screenutil_generator - Do Not Touch ///
/////////////////////////////////////////////////////////////////////////
part of 'responsive_widgets.su.dart';
const _responsiveWidgets = {
'MyThemedApp',
'MyApp',
'HomePageScaffold',
};
... ...
part 'responsive_widgets.g.dart';
get responsiveWidgets => _responsiveWidgets;
... ...
import 'package:example/responsive_widgets.su.dart';
import 'package:example/src/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
... ... @@ -9,9 +10,9 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
// In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it
return ScreenUtilInit(
useInheritedMediaQuery: false,
builder: (_, child) {
return MaterialApp(
responsiveWidgets: responsiveWidgets,
ensureScreenSize: true,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'First Method',
// You can use the library anywhere in the app even in theme
... ... @@ -21,10 +22,8 @@ class MyApp extends StatelessWidget {
.black
.apply(fontSizeFactor: 1),
),
home: child,
);
},
child: const HomePage(title: 'First Method'),
home: const HomePage(title: 'First Method'),
),
);
}
}
... ...
... ... @@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class HomePageScaffold extends StatelessWidget {
class HomePageScaffold extends StatelessWidget with SU {
const HomePageScaffold({Key? key, this.title = ''}) : super(key: key);
void printScreenInformation(BuildContext context) {
... ...
... ... @@ -43,7 +43,7 @@ void main() {
data: currentData,
child: ScreenUtilInit(
designSize: designSize,
builder: (context, child) => MaterialApp(
child: MaterialApp(
home: Material(
child: TextButton(
key: _key,
... ... @@ -68,6 +68,9 @@ void main() {
// Tests with initial screen size
testSize(initialSize);
// Wait for FutureBuilder to be resolved
await tester.pumpAndSettle();
// Click On button to simulate changing screen size
await tap();
// Tests with bigger screen size
... ...
... ... @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico"
// Version
//
#ifdef FLUTTER_BUILD_NUMBER
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else
#define VERSION_AS_NUMBER 1,0,0
#define VERSION_AS_NUMBER 1,0,0,0
#endif
#ifdef FLUTTER_BUILD_NAME
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
#if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING FLUTTER_VERSION
#else
#define VERSION_AS_STRING "1.0.0"
#endif
... ...
... ... @@ -10,3 +10,4 @@ export 'src/r_sizedbox.dart';
export 'src/screen_util.dart';
export 'src/screenutil_init.dart';
export 'src/size_extension.dart';
export 'src/screenutil_mixin.dart';
... ...
import 'dart:collection';
final flutterWidgets = HashSet<String>.from({
'AbsorbPointer',
'Accumulator',
'Action',
'ActionDispatcher',
'ActionListener',
'Actions',
'ActivateAction',
'ActivateIntent',
'Align',
'Alignment',
'AlignmentDirectional',
'AlignmentGeometry',
'AlignmentGeometryTween',
'AlignmentTween',
'AlignTransition',
'AlwaysScrollableScrollPhysics',
'AlwaysStoppedAnimation',
'AndroidView',
'AndroidViewSurface',
'Animatable',
'AnimatedAlign',
'AnimatedBuilder',
'AnimatedContainer',
'AnimatedCrossFade',
'AnimatedDefaultTextStyle',
'AnimatedFractionallySizedBox',
'AnimatedGrid',
'AnimatedGridState',
'AnimatedList',
'AnimatedListState',
'AnimatedModalBarrier',
'AnimatedOpacity',
'AnimatedPadding',
'AnimatedPhysicalModel',
'AnimatedPositioned',
'AnimatedPositionedDirectional',
'AnimatedRotation',
'AnimatedScale',
'AnimatedSize',
'AnimatedSlide',
'AnimatedSwitcher',
'AnimatedWidget',
'AnimatedWidgetBaseState',
'Animation',
'AnimationController',
'AnimationMax',
'AnimationMean',
'AnimationMin',
'AnnotatedRegion',
'AspectRatio',
'AssetBundle',
'AssetBundleImageKey',
'AssetBundleImageProvider',
'AssetImage',
'AsyncSnapshot',
'AutocompleteHighlightedOption',
'AutocompleteNextOptionIntent',
'AutocompletePreviousOptionIntent',
'AutofillGroup',
'AutofillGroupState',
'AutofillHints',
'AutomaticKeepAlive',
'AutomaticNotchedShape',
'BackButtonDispatcher',
'BackButtonListener',
'BackdropFilter',
'BallisticScrollActivity',
'Banner',
'BannerPainter',
'Baseline',
'BaseTapAndDragGestureRecognizer',
'BeveledRectangleBorder',
'BlockSemantics',
'Border',
'BorderDirectional',
'BorderRadius',
'BorderRadiusDirectional',
'BorderRadiusGeometry',
'BorderRadiusTween',
'BorderSide',
'BorderTween',
'BottomNavigationBarItem',
'BouncingScrollPhysics',
'BouncingScrollSimulation',
'BoxBorder',
'BoxConstraints',
'BoxConstraintsTween',
'BoxDecoration',
'BoxPainter',
'BoxScrollView',
'BoxShadow',
'BuildContext',
'Builder',
'BuildOwner',
'ButtonActivateIntent',
'CallbackAction',
'CallbackShortcuts',
'Canvas',
'CapturedThemes',
'CatmullRomCurve',
'CatmullRomSpline',
'Center',
'ChangeNotifier',
'CharacterActivator',
'CharacterRange',
'Characters',
'CheckedModeBanner',
'ChildBackButtonDispatcher',
'CircleBorder',
'CircularNotchedRectangle',
'ClampingScrollPhysics',
'ClampingScrollSimulation',
'ClipboardStatusNotifier',
'ClipContext',
'ClipOval',
'ClipPath',
'ClipRect',
'ClipRRect',
'Color',
'ColoredBox',
'ColorFilter',
'ColorFiltered',
'ColorProperty',
'ColorSwatch',
'ColorTween',
'Column',
'ComponentElement',
'CompositedTransformFollower',
'CompositedTransformTarget',
'CompoundAnimation',
'ConstantTween',
'ConstrainedBox',
'ConstrainedLayoutBuilder',
'ConstraintsTransformBox',
'Container',
'ContentInsertionConfiguration',
'ContextAction',
'ContextMenuButtonItem',
'ContextMenuController',
'ContinuousRectangleBorder',
'CopySelectionTextIntent',
'Cubic',
'Curve',
'Curve2D',
'Curve2DSample',
'CurvedAnimation',
'Curves',
'CurveTween',
'CustomClipper',
'CustomMultiChildLayout',
'CustomPaint',
'CustomPainter',
'CustomPainterSemantics',
'CustomScrollView',
'CustomSingleChildLayout',
'DebugCreator',
'DecoratedBox',
'DecoratedBoxTransition',
'Decoration',
'DecorationImage',
'DecorationImagePainter',
'DecorationTween',
'DefaultAssetBundle',
'DefaultPlatformMenuDelegate',
'DefaultSelectionStyle',
'DefaultTextEditingShortcuts',
'DefaultTextHeightBehavior',
'DefaultTextStyle',
'DefaultTextStyleTransition',
'DefaultTransitionDelegate',
'DefaultWidgetsLocalizations',
'DeleteCharacterIntent',
'DeleteToLineBreakIntent',
'DeleteToNextWordBoundaryIntent',
'DesktopTextSelectionToolbarLayoutDelegate',
'DevToolsDeepLinkProperty',
'DiagnosticsNode',
'DirectionalCaretMovementIntent',
'DirectionalFocusAction',
'DirectionalFocusIntent',
'Directionality',
'DirectionalTextEditingIntent',
'DismissAction',
'Dismissible',
'DismissIntent',
'DismissUpdateDetails',
'DisplayFeatureSubScreen',
'DisposableBuildContext',
'DoNothingAction',
'DoNothingAndStopPropagationIntent',
'DoNothingAndStopPropagationTextIntent',
'DoNothingIntent',
'DragDownDetails',
'DragEndDetails',
'Draggable',
'DraggableDetails',
'DraggableScrollableActuator',
'DraggableScrollableController',
'DraggableScrollableNotification',
'DraggableScrollableSheet',
'DragScrollActivity',
'DragStartDetails',
'DragTarget',
'DragTargetDetails',
'DragUpdateDetails',
'DrivenScrollActivity',
'DualTransitionBuilder',
'EdgeDraggingAutoScroller',
'EdgeInsets',
'EdgeInsetsDirectional',
'EdgeInsetsGeometry',
'EdgeInsetsGeometryTween',
'EdgeInsetsTween',
'EditableText',
'EditableTextState',
'ElasticInCurve',
'ElasticInOutCurve',
'ElasticOutCurve',
'Element',
'EmptyTextSelectionControls',
'ErrorDescription',
'ErrorHint',
'ErrorSummary',
'ErrorWidget',
'ExactAssetImage',
'ExcludeFocus',
'ExcludeFocusTraversal',
'ExcludeSemantics',
'Expanded',
'ExpandSelectionToDocumentBoundaryIntent',
'ExpandSelectionToLineBreakIntent',
'ExtendSelectionByCharacterIntent',
'ExtendSelectionByPageIntent',
'ExtendSelectionToDocumentBoundaryIntent',
'ExtendSelectionToLineBreakIntent',
'ExtendSelectionToNextParagraphBoundaryIntent',
'ExtendSelectionToNextParagraphBoundaryOrCaretLocationIntent',
'ExtendSelectionToNextWordBoundaryIntent',
'ExtendSelectionToNextWordBoundaryOrCaretLocationIntent',
'ExtendSelectionVerticallyToAdjacentLineIntent',
'ExtendSelectionVerticallyToAdjacentPageIntent',
'FadeInImage',
'FadeTransition',
'FileImage',
'FittedBox',
'FittedSizes',
'FixedColumnWidth',
'FixedExtentMetrics',
'FixedExtentScrollController',
'FixedExtentScrollPhysics',
'FixedScrollMetrics',
'Flex',
'FlexColumnWidth',
'Flexible',
'FlippedCurve',
'FlippedTweenSequence',
'Flow',
'FlowDelegate',
'FlowPaintingContext',
'FlutterErrorDetails',
'FlutterLogoDecoration',
'Focus',
'FocusableActionDetector',
'FocusAttachment',
'FocusManager',
'FocusNode',
'FocusOrder',
'FocusScope',
'FocusScopeNode',
'FocusTraversalGroup',
'FocusTraversalOrder',
'FocusTraversalPolicy',
'FontWeight',
'ForcePressDetails',
'Form',
'FormField',
'FormFieldState',
'FormState',
'FractionallySizedBox',
'FractionalOffset',
'FractionalOffsetTween',
'FractionalTranslation',
'FractionColumnWidth',
'FutureBuilder',
'GestureDetector',
'GestureRecognizerFactory',
'GestureRecognizerFactoryWithHandlers',
'GlobalKey',
'GlobalObjectKey',
'GlowingOverscrollIndicator',
'Gradient',
'GradientRotation',
'GradientTransform',
'GridPaper',
'GridView',
'Hero',
'HeroController',
'HeroControllerScope',
'HeroMode',
'HoldScrollActivity',
'HSLColor',
'HSVColor',
'HtmlElementView',
'Icon',
'IconData',
'IconDataProperty',
'IconTheme',
'IconThemeData',
'IdleScrollActivity',
'IgnorePointer',
'Image',
'ImageCache',
'ImageCacheStatus',
'ImageChunkEvent',
'ImageConfiguration',
'ImageFiltered',
'ImageIcon',
'ImageInfo',
'ImageProvider',
'ImageShader',
'ImageSizeInfo',
'ImageStream',
'ImageStreamCompleter',
'ImageStreamCompleterHandle',
'ImageStreamListener',
'ImageTilingInfo',
'ImplicitlyAnimatedWidget',
'ImplicitlyAnimatedWidgetState',
'IndexedSemantics',
'IndexedSlot',
'IndexedStack',
'InheritedElement',
'InheritedModel',
'InheritedModelElement',
'InheritedNotifier',
'InheritedTheme',
'InheritedWidget',
'InlineSpan',
'InlineSpanSemanticsInformation',
'InspectorSelection',
'InspectorSerializationDelegate',
'Intent',
'InteractiveViewer',
'Interval',
'IntrinsicColumnWidth',
'IntrinsicHeight',
'IntrinsicWidth',
'IntTween',
'KeepAlive',
'KeepAliveHandle',
'KeepAliveNotification',
'Key',
'KeyboardInsertedContent',
'KeyboardListener',
'KeyedSubtree',
'KeyEvent',
'KeySet',
'LabeledGlobalKey',
'LayerLink',
'LayoutBuilder',
'LayoutChangedNotification',
'LayoutId',
'LeafRenderObjectElement',
'LeafRenderObjectWidget',
'LexicalFocusOrder',
'LimitedBox',
'LinearBorder',
'LinearBorderEdge',
'LinearGradient',
'ListBody',
'Listenable',
'ListenableBuilder',
'Listener',
'ListView',
'ListWheelChildBuilderDelegate',
'ListWheelChildDelegate',
'ListWheelChildListDelegate',
'ListWheelChildLoopingListDelegate',
'ListWheelElement',
'ListWheelScrollView',
'ListWheelViewport',
'Locale',
'LocalHistoryEntry',
'Localizations',
'LocalizationsDelegate',
'LocalKey',
'LogicalKeySet',
'LongPressDraggable',
'LongPressEndDetails',
'LongPressMoveUpdateDetails',
'LongPressStartDetails',
'LookupBoundary',
'MagnifierController',
'MagnifierDecoration',
'MagnifierInfo',
'MaskFilter',
'Matrix4',
'Matrix4Tween',
'MatrixUtils',
'MaxColumnWidth',
'MediaQuery',
'MediaQueryData',
'MemoryImage',
'MergeSemantics',
'MetaData',
'MinColumnWidth',
'ModalBarrier',
'ModalRoute',
'MouseCursor',
'MouseRegion',
'MultiChildLayoutDelegate',
'MultiChildRenderObjectElement',
'MultiChildRenderObjectWidget',
'MultiFrameImageStreamCompleter',
'MultiSelectableSelectionContainerDelegate',
'NavigationToolbar',
'Navigator',
'NavigatorObserver',
'NavigatorState',
'NestedScrollView',
'NestedScrollViewState',
'NestedScrollViewViewport',
'NetworkImage',
'NeverScrollableScrollPhysics',
'NextFocusAction',
'NextFocusIntent',
'NotchedShape',
'Notification',
'NotificationListener',
'NumericFocusOrder',
'ObjectKey',
'Offset',
'Offstage',
'OneFrameImageStreamCompleter',
'Opacity',
'OrderedTraversalPolicy',
'OrientationBuilder',
'OutlinedBorder',
'OvalBorder',
'OverflowBar',
'OverflowBox',
'Overlay',
'OverlayEntry',
'OverlayPortal',
'OverlayPortalController',
'OverlayRoute',
'OverlayState',
'OverscrollIndicatorNotification',
'OverscrollNotification',
'Padding',
'Page',
'PageController',
'PageMetrics',
'PageRoute',
'PageRouteBuilder',
'PageScrollPhysics',
'PageStorage',
'PageStorageBucket',
'PageStorageKey',
'PageView',
'Paint',
'PaintingContext',
'ParametricCurve',
'ParentDataElement',
'ParentDataWidget',
'PasteTextIntent',
'Path',
'PerformanceOverlay',
'PhysicalModel',
'PhysicalShape',
'Placeholder',
'PlaceholderDimensions',
'PlaceholderSpan',
'PlatformMenu',
'PlatformMenuBar',
'PlatformMenuDelegate',
'PlatformMenuItem',
'PlatformMenuItemGroup',
'PlatformProvidedMenuItem',
'PlatformRouteInformationProvider',
'PlatformSelectableRegionContextMenu',
'PlatformViewCreationParams',
'PlatformViewLink',
'PlatformViewSurface',
'PointerCancelEvent',
'PointerDownEvent',
'PointerEvent',
'PointerMoveEvent',
'PointerUpEvent',
'PopupRoute',
'Positioned',
'PositionedDirectional',
'PositionedTransition',
'PreferredSize',
'PreferredSizeWidget',
'PreviousFocusAction',
'PreviousFocusIntent',
'PrimaryScrollController',
'PrioritizedAction',
'PrioritizedIntents',
'ProxyAnimation',
'ProxyElement',
'ProxyWidget',
'RadialGradient',
'Radius',
'RangeMaintainingScrollPhysics',
'RawAutocomplete',
'RawDialogRoute',
'RawGestureDetector',
'RawGestureDetectorState',
'RawImage',
'RawKeyboardListener',
'RawKeyEvent',
'RawMagnifier',
'RawScrollbar',
'RawScrollbarState',
'ReadingOrderTraversalPolicy',
'Rect',
'RectTween',
'RedoTextIntent',
'RelativePositionedTransition',
'RelativeRect',
'RelativeRectTween',
'RenderBox',
'RenderNestedScrollViewViewport',
'RenderObject',
'RenderObjectElement',
'RenderObjectToWidgetAdapter',
'RenderObjectToWidgetElement',
'RenderObjectWidget',
'RenderSemanticsGestureHandler',
'RenderSliverOverlapAbsorber',
'RenderSliverOverlapInjector',
'RenderTapRegion',
'RenderTapRegionSurface',
'ReorderableDelayedDragStartListener',
'ReorderableDragStartListener',
'ReorderableList',
'ReorderableListState',
'RepaintBoundary',
'ReplaceTextIntent',
'RequestFocusAction',
'RequestFocusIntent',
'ResizeImage',
'ResizeImageKey',
'RestorableBool',
'RestorableBoolN',
'RestorableChangeNotifier',
'RestorableDateTime',
'RestorableDateTimeN',
'RestorableDouble',
'RestorableDoubleN',
'RestorableEnum',
'RestorableEnumN',
'RestorableInt',
'RestorableIntN',
'RestorableListenable',
'RestorableNum',
'RestorableNumN',
'RestorableProperty',
'RestorableRouteFuture',
'RestorableString',
'RestorableStringN',
'RestorableTextEditingController',
'RestorableValue',
'RestorationBucket',
'RestorationScope',
'ReverseAnimation',
'ReverseTween',
'RichText',
'RootBackButtonDispatcher',
'RootRenderObjectElement',
'RootRestorationScope',
'RotatedBox',
'RotationTransition',
'RoundedRectangleBorder',
'Route',
'RouteAware',
'RouteInformation',
'RouteInformationParser',
'RouteInformationProvider',
'RouteObserver',
'Router',
'RouterConfig',
'RouterDelegate',
'RouteSettings',
'RouteTransitionRecord',
'Row',
'RRect',
'RSTransform',
'SafeArea',
'SawTooth',
'ScaleEndDetails',
'ScaleStartDetails',
'ScaleTransition',
'ScaleUpdateDetails',
'Scrollable',
'ScrollableDetails',
'ScrollableState',
'ScrollAction',
'ScrollActivity',
'ScrollActivityDelegate',
'ScrollAwareImageProvider',
'ScrollbarPainter',
'ScrollBehavior',
'ScrollConfiguration',
'ScrollContext',
'ScrollController',
'ScrollDragController',
'ScrollEndNotification',
'ScrollHoldController',
'ScrollIncrementDetails',
'ScrollIntent',
'ScrollMetricsNotification',
'ScrollNotification',
'ScrollNotificationObserver',
'ScrollNotificationObserverState',
'ScrollPhysics',
'ScrollPosition',
'ScrollPositionWithSingleContext',
'ScrollSpringSimulation',
'ScrollStartNotification',
'ScrollToDocumentBoundaryIntent',
'ScrollUpdateNotification',
'ScrollView',
'SelectableRegion',
'SelectableRegionState',
'SelectAction',
'SelectAllTextIntent',
'SelectIntent',
'SelectionContainer',
'SelectionContainerDelegate',
'SelectionOverlay',
'SelectionRegistrarScope',
'Semantics',
'SemanticsDebugger',
'SemanticsGestureDelegate',
'Shader',
'ShaderMask',
'ShaderWarmUp',
'Shadow',
'ShapeBorder',
'ShapeBorderClipper',
'ShapeDecoration',
'SharedAppData',
'ShortcutActivator',
'ShortcutManager',
'ShortcutMapProperty',
'ShortcutRegistrar',
'ShortcutRegistry',
'ShortcutRegistryEntry',
'Shortcuts',
'ShortcutSerialization',
'ShrinkWrappingViewport',
'Simulation',
'SingleActivator',
'SingleChildLayoutDelegate',
'SingleChildRenderObjectElement',
'SingleChildRenderObjectWidget',
'SingleChildScrollView',
'Size',
'SizeChangedLayoutNotification',
'SizeChangedLayoutNotifier',
'SizedBox',
'SizedOverflowBox',
'SizeTransition',
'SizeTween',
'SlideTransition',
'SliverAnimatedGrid',
'SliverAnimatedGridState',
'SliverAnimatedList',
'SliverAnimatedListState',
'SliverAnimatedOpacity',
'SliverChildBuilderDelegate',
'SliverChildDelegate',
'SliverChildListDelegate',
'SliverFadeTransition',
'SliverFillRemaining',
'SliverFillViewport',
'SliverFixedExtentList',
'SliverGrid',
'SliverGridDelegate',
'SliverGridDelegateWithFixedCrossAxisCount',
'SliverGridDelegateWithMaxCrossAxisExtent',
'SliverIgnorePointer',
'SliverLayoutBuilder',
'SliverList',
'SliverMultiBoxAdaptorElement',
'SliverMultiBoxAdaptorWidget',
'SliverOffstage',
'SliverOpacity',
'SliverOverlapAbsorber',
'SliverOverlapAbsorberHandle',
'SliverOverlapInjector',
'SliverPadding',
'SliverPersistentHeader',
'SliverPersistentHeaderDelegate',
'SliverPrototypeExtentList',
'SliverReorderableList',
'SliverReorderableListState',
'SliverSafeArea',
'SliverToBoxAdapter',
'SliverVisibility',
'SliverWithKeepAliveWidget',
'SlottedRenderObjectElement',
'SnapshotController',
'SnapshotPainter',
'SnapshotWidget',
'Spacer',
'SpellCheckConfiguration',
'SpringDescription',
'Stack',
'StadiumBorder',
'StarBorder',
'State',
'StatefulBuilder',
'StatefulElement',
'StatefulWidget',
'StatelessElement',
'StatelessWidget',
'StatusTransitionWidget',
'StepTween',
'StreamBuilder',
'StreamBuilderBase',
'StretchingOverscrollIndicator',
'StrutStyle',
'SweepGradient',
'SystemMouseCursors',
'Table',
'TableBorder',
'TableCell',
'TableColumnWidth',
'TableRow',
'TapAndDragGestureRecognizer',
'TapAndHorizontalDragGestureRecognizer',
'TapAndPanGestureRecognizer',
'TapDownDetails',
'TapDragDownDetails',
'TapDragEndDetails',
'TapDragStartDetails',
'TapDragUpdateDetails',
'TapDragUpDetails',
'TapRegion',
'TapRegionRegistry',
'TapRegionSurface',
'TapUpDetails',
'Text',
'TextAlignVertical',
'TextBox',
'TextDecoration',
'TextEditingController',
'TextEditingValue',
'TextFieldTapRegion',
'TextHeightBehavior',
'TextInputType',
'TextMagnifierConfiguration',
'TextPainter',
'TextPosition',
'TextRange',
'TextSelection',
'TextSelectionControls',
'TextSelectionGestureDetector',
'TextSelectionGestureDetectorBuilder',
'TextSelectionGestureDetectorBuilderDelegate',
'TextSelectionOverlay',
'TextSelectionPoint',
'TextSelectionToolbarAnchors',
'TextSelectionToolbarLayoutDelegate',
'TextSpan',
'TextStyle',
'TextStyleTween',
'Texture',
'ThreePointCubic',
'Threshold',
'TickerFuture',
'TickerMode',
'TickerProvider',
'Title',
'Tolerance',
'ToolbarItemsParentData',
'ToolbarOptions',
'TrackingScrollController',
'TrainHoppingAnimation',
'Transform',
'TransformationController',
'TransformProperty',
'TransitionDelegate',
'TransitionRoute',
'TransposeCharactersIntent',
'Tween',
'TweenAnimationBuilder',
'TweenSequence',
'TweenSequenceItem',
'UiKitView',
'UnconstrainedBox',
'UndoHistory',
'UndoHistoryController',
'UndoHistoryState',
'UndoHistoryValue',
'UndoTextIntent',
'UniqueKey',
'UniqueWidget',
'UnmanagedRestorationScope',
'UpdateSelectionIntent',
'UserScrollNotification',
'ValueKey',
'ValueListenableBuilder',
'ValueNotifier',
'Velocity',
'View',
'Viewport',
'Visibility',
'VoidCallbackAction',
'VoidCallbackIntent',
'Widget',
'WidgetInspector',
'WidgetOrderTraversalPolicy',
'WidgetsApp',
'WidgetsBindingObserver',
'WidgetsFlutterBinding',
'WidgetsLocalizations',
'WidgetSpan',
'WidgetToRenderBoxAdapter',
'WillPopScope',
'WordBoundary',
'Wrap'
});
... ...
... ... @@ -4,11 +4,12 @@
*/
import 'dart:math' show min, max;
import 'dart:ui' show FlutterView;
import 'dart:async' show Completer;
import 'dart:ui' as ui show FlutterView;
import 'package:flutter/widgets.dart';
typedef FontSizeResolver = double Function(num fontSize, ScreenUtil instance);
class ScreenUtil {
static const Size defaultSize = Size(360, 690);
static ScreenUtil _instance = ScreenUtil._();
... ... @@ -20,17 +21,14 @@ class ScreenUtil {
///屏幕方向
late Orientation _orientation;
late double _screenWidth;
late double _screenHeight;
late bool _minTextAdapt;
BuildContext? _context;
late MediaQueryData _data;
late bool _splitScreenMode;
FontSizeResolver? fontSizeResolver;
ScreenUtil._();
factory ScreenUtil() {
return _instance;
}
factory ScreenUtil() => _instance;
/// Manually wait for window size to be initialized
///
... ... @@ -55,19 +53,25 @@ class ScreenUtil {
/// )
/// ```
static Future<void> ensureScreenSize([
FlutterView? window,
ui.FlutterView? window,
Duration duration = const Duration(milliseconds: 10),
]) async {
final binding = WidgetsFlutterBinding.ensureInitialized();
window ??= WidgetsBinding.instance.platformDispatcher.implicitView;
if (window?.physicalGeometry.isEmpty == true) {
return Future.delayed(duration, () async {
binding.deferFirstFrame();
await ensureScreenSize(window, duration);
return binding.allowFirstFrame();
});
await Future.doWhile(() {
if (window == null) {
window = binding.platformDispatcher.implicitView;
}
if (window == null || window!.physicalSize.isEmpty) {
return Future.delayed(duration, () => true);
}
return false;
});
binding.allowFirstFrame();
}
Set<Element>? _elementsToRebuild;
... ... @@ -88,44 +92,78 @@ class ScreenUtil {
}
}
/// Initializing the library.
static Future<void> init(BuildContext context,
{Size designSize = defaultSize,
bool splitScreenMode = false,
bool minTextAdapt = false,
bool scaleByHeight = false}) async {
final mediaQueryContext =
context.getElementForInheritedWidgetOfExactType<MediaQuery>();
final initCompleter = Completer<void>();
static void configure({
MediaQueryData? data,
Size? designSize,
bool? splitScreenMode,
bool? minTextAdapt,
FontSizeResolver? fontSizeResolver,
}) {
try {
if (data != null)
_instance._data = data;
else
data = _instance._data;
WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((_) {
mediaQueryContext?.visitChildElements((el) => _instance._context = el);
if (_instance._context != null) initCompleter.complete();
});
if (designSize != null)
_instance._uiSize = designSize;
else
designSize = _instance._uiSize;
} catch (_) {
throw Exception(
'You must either use ScreenUtil.init or ScreenUtilInit first');
}
final deviceData = MediaQuery.maybeOf(context).nonEmptySizeOrNull();
final MediaQueryData? deviceData = data.nonEmptySizeOrNull();
final Size deviceSize = deviceData?.size ?? designSize;
final deviceSize = deviceData?.size ?? designSize;
final orientation = deviceData?.orientation ??
(deviceSize.width > deviceSize.height
? Orientation.landscape
: Orientation.portrait);
_instance
.._context = scaleByHeight ? null : context
.._uiSize = designSize
.._splitScreenMode = splitScreenMode
.._minTextAdapt = minTextAdapt
.._orientation = orientation
.._screenWidth = scaleByHeight
? (deviceSize.height * designSize.width) / designSize.height
: deviceSize.width
.._screenHeight = deviceSize.height;
..fontSizeResolver = fontSizeResolver ?? _instance.fontSizeResolver
.._minTextAdapt = minTextAdapt ?? _instance._minTextAdapt
.._splitScreenMode = splitScreenMode ?? _instance._splitScreenMode
.._orientation = orientation;
_instance._elementsToRebuild?.forEach((el) => el.markNeedsBuild());
}
/// Initializing the library.
static void init(
BuildContext context, {
Size designSize = defaultSize,
bool splitScreenMode = false,
bool minTextAdapt = false,
FontSizeResolver? fontSizeResolver,
}) {
return configure(
data: MediaQuery.maybeOf(context),
designSize: designSize,
splitScreenMode: splitScreenMode,
minTextAdapt: minTextAdapt,
fontSizeResolver: fontSizeResolver,
);
}
return initCompleter.future;
static Future<void> ensureScreenSizeAndInit(
BuildContext context, {
Size designSize = defaultSize,
bool splitScreenMode = false,
bool minTextAdapt = false,
FontSizeResolver? fontSizeResolver,
}) {
return ScreenUtil.ensureScreenSize().then((_) {
return configure(
data: MediaQuery.maybeOf(context),
designSize: designSize,
minTextAdapt: minTextAdapt,
splitScreenMode: splitScreenMode,
fontSizeResolver: fontSizeResolver,
);
});
}
///获取屏幕方向
... ... @@ -134,39 +172,33 @@ class ScreenUtil {
/// 每个逻辑像素的字体像素数,字体的缩放比例
/// The number of font pixels for each logical pixel.
double get textScaleFactor =>
_context != null ? MediaQuery.of(_context!).textScaleFactor : 1;
double get textScaleFactor => _data.textScaleFactor;
/// 设备的像素密度
/// The size of the media in logical pixels (e.g, the size of the screen).
double? get pixelRatio =>
_context != null ? MediaQuery.of(_context!).devicePixelRatio : 1;
double? get pixelRatio => _data.devicePixelRatio;
/// 当前设备宽度 dp
/// The horizontal extent of this size.
double get screenWidth =>
_context != null ? MediaQuery.of(_context!).size.width : _screenWidth;
double get screenWidth => _data.size.width;
///当前设备高度 dp
///The vertical extent of this size. dp
double get screenHeight =>
_context != null ? MediaQuery.of(_context!).size.height : _screenHeight;
double get screenHeight => _data.size.height;
/// 状态栏高度 dp 刘海屏会更高
/// The offset from the top, in dp
double get statusBarHeight =>
_context == null ? 0 : MediaQuery.of(_context!).padding.top;
double get statusBarHeight => _data.padding.top;
/// 底部安全区距离 dp
/// The offset from the bottom, in dp
double get bottomBarHeight =>
_context == null ? 0 : MediaQuery.of(_context!).padding.bottom;
double get bottomBarHeight => _data.padding.bottom;
/// 实际尺寸与UI设计的比例
/// The ratio of actual width to UI design
double get scaleWidth => screenWidth / _uiSize.width;
/// /// The ratio of actual height to UI design
/// The ratio of actual height to UI design
double get scaleHeight =>
(_splitScreenMode ? max(screenHeight, 700) : screenHeight) /
_uiSize.height;
... ... @@ -195,24 +227,44 @@ class ScreenUtil {
///Adapt according to the smaller of width or height
double radius(num r) => r * min(scaleWidth, scaleHeight);
/// Adapt according to the both width and height
double diagonal(num d) => d * scaleHeight * scaleWidth;
/// Adapt according to the maximum value of scale width and scale height
double diameter(num d) => d * max(scaleWidth, scaleHeight);
///字体大小适配方法
///- [fontSize] UI设计上字体的大小,单位dp.
///Font size adaptation method
///- [fontSize] The size of the font on the UI design, in dp.
double setSp(num fontSize) => fontSize * scaleText;
double setSp(num fontSize) =>
fontSizeResolver?.call(fontSize, _instance) ?? fontSize * scaleText;
Widget setVerticalSpacing(num height) => SizedBox(height: setHeight(height));
SizedBox setVerticalSpacing(num height) =>
SizedBox(height: setHeight(height));
Widget setVerticalSpacingFromWidth(num height) =>
SizedBox setVerticalSpacingFromWidth(num height) =>
SizedBox(height: setWidth(height));
Widget setHorizontalSpacing(num width) => SizedBox(width: setWidth(width));
SizedBox setHorizontalSpacing(num width) => SizedBox(width: setWidth(width));
Widget setHorizontalSpacingRadius(num width) =>
SizedBox setHorizontalSpacingRadius(num width) =>
SizedBox(width: radius(width));
Widget setVerticalSpacingRadius(num height) =>
SizedBox setVerticalSpacingRadius(num height) =>
SizedBox(height: radius(height));
SizedBox setHorizontalSpacingDiameter(num width) =>
SizedBox(width: diameter(width));
SizedBox setVerticalSpacingDiameter(num height) =>
SizedBox(height: diameter(height));
SizedBox setHorizontalSpacingDiagonal(num width) =>
SizedBox(width: diagonal(width));
SizedBox setVerticalSpacingDiagonal(num height) =>
SizedBox(height: diagonal(height));
}
extension on MediaQueryData? {
... ...
import 'dart:async';
import 'dart:collection';
import 'package:flutter/widgets.dart';
import './_flutter_widgets.dart';
import 'screenutil_mixin.dart';
import 'screen_util.dart';
typedef RebuildFactor = bool Function(MediaQueryData old, MediaQueryData data);
... ... @@ -9,9 +14,7 @@ typedef ScreenUtilInitBuilder = Widget Function(
Widget? child,
);
class RebuildFactors {
const RebuildFactors._();
abstract class RebuildFactors {
static bool size(MediaQueryData old, MediaQueryData data) {
return old.size != data.size;
}
... ... @@ -24,35 +27,69 @@ class RebuildFactors {
return old.viewInsets != data.viewInsets;
}
static bool all(MediaQueryData old, MediaQueryData data) {
static bool change(MediaQueryData old, MediaQueryData data) {
return old != data;
}
static bool always(MediaQueryData _, MediaQueryData __) {
return true;
}
static bool none(MediaQueryData _, MediaQueryData __) {
return false;
}
}
abstract class FontSizeResolvers {
static double width(num fontSize, ScreenUtil instance) {
return instance.setWidth(fontSize);
}
static double height(num fontSize, ScreenUtil instance) {
return instance.setHeight(fontSize);
}
static double raduis(num fontSize, ScreenUtil instance) {
return instance.radius(fontSize);
}
static double diameter(num fontSize, ScreenUtil instance) {
return instance.diameter(fontSize);
}
static double diagonal(num fontSize, ScreenUtil instance) {
return instance.diagonal(fontSize);
}
}
class ScreenUtilInit extends StatefulWidget {
/// A helper widget that initializes [ScreenUtil]
const ScreenUtilInit(
{Key? key,
required this.builder,
const ScreenUtilInit({
Key? key,
this.builder,
this.child,
this.rebuildFactor = RebuildFactors.size,
this.designSize = ScreenUtil.defaultSize,
this.splitScreenMode = false,
this.minTextAdapt = false,
this.useInheritedMediaQuery = false,
this.scaleByHeight = false})
: super(key: key);
this.ensureScreenSize,
this.responsiveWidgets,
this.fontSizeResolver = FontSizeResolvers.width,
}) : super(key: key);
final ScreenUtilInitBuilder builder;
final ScreenUtilInitBuilder? builder;
final Widget? child;
final bool splitScreenMode;
final bool minTextAdapt;
final bool useInheritedMediaQuery;
final bool scaleByHeight;
final bool? ensureScreenSize;
final RebuildFactor rebuildFactor;
final FontSizeResolver fontSizeResolver;
/// The [Size] of the device in the design draft, in dp
final Size designSize;
final Iterable<String>? responsiveWidgets;
@override
State<ScreenUtilInit> createState() => _ScreenUtilInitState();
... ... @@ -60,127 +97,103 @@ class ScreenUtilInit extends StatefulWidget {
class _ScreenUtilInitState extends State<ScreenUtilInit>
with WidgetsBindingObserver {
final _canMarkedToBuild = HashSet<String>();
MediaQueryData? _mediaQueryData;
final _binding = WidgetsBinding.instance;
final _screenSizeCompleter = Completer<void>();
bool wrappedInMediaQuery = false;
@override
void initState() {
if (widget.responsiveWidgets != null) {
_canMarkedToBuild.addAll(widget.responsiveWidgets!);
}
_validateSize().then(_screenSizeCompleter.complete);
super.initState();
_binding.addObserver(this);
}
WidgetsBinding get binding => WidgetsFlutterBinding.ensureInitialized();
@override
void didChangeMetrics() {
super.didChangeMetrics();
_revalidate();
}
MediaQueryData get mediaQueryData => _mediaQueryData!;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_revalidate();
}
MediaQueryData get newData {
final data = MediaQuery.maybeOf(context);
MediaQueryData? _newData() {
MediaQueryData? mq = MediaQuery.maybeOf(context);
if (mq == null) mq = MediaQueryData.fromView(View.of(context));
if (data != null) {
if (widget.useInheritedMediaQuery) {
wrappedInMediaQuery = true;
return mq;
}
return data;
Future<void> _validateSize() async {
if (widget.ensureScreenSize ?? false) return ScreenUtil.ensureScreenSize();
}
return MediaQueryData.fromView(View.of(context));
void _markNeedsBuildIfAllowed(Element el) {
final widgetName = el.widget.runtimeType.toString();
final allowed = widget is SU ||
_canMarkedToBuild.contains(widgetName) ||
!(widgetName.startsWith('_') || flutterWidgets.contains(widgetName));
if (allowed) el.markNeedsBuild();
}
_updateTree(Element el) {
el.markNeedsBuild();
void _updateTree(Element el) {
_markNeedsBuildIfAllowed(el);
el.visitChildren(_updateTree);
}
@override
void initState() {
super.initState();
binding.addObserver(this);
}
void _revalidate([void Function()? callback]) {
final oldData = _mediaQueryData;
final newData = _newData();
@override
void didChangeMetrics() {
final old = _mediaQueryData!;
final data = newData;
if (newData == null) return;
if (widget.scaleByHeight || widget.rebuildFactor(old, data)) {
_mediaQueryData = data;
if (oldData == null || widget.rebuildFactor(oldData, newData)) {
setState(() {
_mediaQueryData = newData;
_updateTree(context as Element);
callback?.call();
});
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_mediaQueryData == null) _mediaQueryData = newData;
didChangeMetrics();
}
Widget build(BuildContext context) {
final mq = _mediaQueryData;
@override
void dispose() {
binding.removeObserver(this);
super.dispose();
}
if (mq == null) return const SizedBox.shrink();
@override
Widget build(BuildContext _context) {
if (mediaQueryData.size == Size.zero) return const SizedBox.shrink();
if (!wrappedInMediaQuery) {
return MediaQuery(
data: mediaQueryData,
child: Builder(
builder: (__context) {
ScreenUtil.init(
__context,
return FutureBuilder<void>(
future: _screenSizeCompleter.future,
builder: (c, snapshot) {
ScreenUtil.configure(
data: mq,
designSize: widget.designSize,
splitScreenMode: widget.splitScreenMode,
minTextAdapt: widget.minTextAdapt,
scaleByHeight: widget.scaleByHeight,
);
final deviceData = MediaQuery.maybeOf(__context);
final deviceSize = deviceData?.size ?? widget.designSize;
return MediaQuery(
data: MediaQueryData.fromView(View.of(__context)),
child: Container(
width: deviceSize.width,
height: deviceSize.height,
child: FittedBox(
fit: BoxFit.none,
alignment: Alignment.center,
child: Container(
width: widget.scaleByHeight
? (deviceSize.height * widget.designSize.width) /
widget.designSize.height
: deviceSize.width,
height: deviceSize.height,
child: widget.builder(__context, widget.child),
),
),
),
fontSizeResolver: widget.fontSizeResolver,
);
if (snapshot.connectionState == ConnectionState.done) {
return widget.builder?.call(context, widget.child) ?? widget.child!;
}
return const SizedBox.shrink();
},
),
);
}
ScreenUtil.init(
_context,
designSize: widget.designSize,
splitScreenMode: widget.splitScreenMode,
minTextAdapt: widget.minTextAdapt,
scaleByHeight: widget.scaleByHeight,
);
final deviceData = MediaQuery.maybeOf(_context);
final deviceSize = deviceData?.size ?? widget.designSize;
return Container(
width: deviceSize.width,
height: deviceSize.height,
child: FittedBox(
fit: BoxFit.none,
alignment: Alignment.center,
child: Container(
width: widget.scaleByHeight
? (deviceSize.height * widget.designSize.width) /
widget.designSize.height
: deviceSize.width,
height: deviceSize.height,
child: widget.builder(_context, widget.child),
),
),
);
@override
void dispose() {
_binding.removeObserver(this);
super.dispose();
}
}
... ...
import 'package:flutter/widgets.dart';
mixin SU on Widget {}
... ...
... ... @@ -14,6 +14,12 @@ extension SizeExtension on num {
///[ScreenUtil.radius]
double get r => ScreenUtil().radius(this);
///[ScreenUtil.diagonal]
double get dg => ScreenUtil().diagonal(this);
///[ScreenUtil.diameter]
double get dm => ScreenUtil().diameter(this);
///[ScreenUtil.setSp]
double get sp => ScreenUtil().setSp(this);
... ... @@ -36,22 +42,38 @@ extension SizeExtension on num {
double get sh => ScreenUtil().screenHeight * this;
///[ScreenUtil.setHeight]
Widget get verticalSpace => ScreenUtil().setVerticalSpacing(this);
SizedBox get verticalSpace => ScreenUtil().setVerticalSpacing(this);
///[ScreenUtil.setVerticalSpacingFromWidth]
Widget get verticalSpaceFromWidth =>
SizedBox get verticalSpaceFromWidth =>
ScreenUtil().setVerticalSpacingFromWidth(this);
///[ScreenUtil.setWidth]
Widget get horizontalSpace => ScreenUtil().setHorizontalSpacing(this);
SizedBox get horizontalSpace => ScreenUtil().setHorizontalSpacing(this);
///[ScreenUtil.radius]
Widget get horizontalSpaceRadius =>
SizedBox get horizontalSpaceRadius =>
ScreenUtil().setHorizontalSpacingRadius(this);
///[ScreenUtil.radius]
Widget get verticalSpacingRadius =>
SizedBox get verticalSpacingRadius =>
ScreenUtil().setVerticalSpacingRadius(this);
///[ScreenUtil.diameter]
SizedBox get horizontalSpaceDiameter =>
ScreenUtil().setHorizontalSpacingDiameter(this);
///[ScreenUtil.diameter]
SizedBox get verticalSpacingDiameter =>
ScreenUtil().setVerticalSpacingDiameter(this);
///[ScreenUtil.diagonal]
SizedBox get horizontalSpaceDiagonal =>
ScreenUtil().setHorizontalSpacingDiagonal(this);
///[ScreenUtil.diagonal]
SizedBox get verticalSpacingDiagonal =>
ScreenUtil().setVerticalSpacingDiagonal(this);
}
extension EdgeInsetsExtension on EdgeInsets {
... ... @@ -63,6 +85,20 @@ extension EdgeInsetsExtension on EdgeInsets {
left: left.r,
);
EdgeInsets get dm => copyWith(
top: top.dm,
bottom: bottom.dm,
right: right.dm,
left: left.dm,
);
EdgeInsets get dg => copyWith(
top: top.dg,
bottom: bottom.dg,
right: right.dg,
left: left.dg,
);
EdgeInsets get w => copyWith(
top: top.w,
bottom: bottom.w,
... ... @@ -106,6 +142,10 @@ extension RaduisExtension on Radius {
/// Creates adapt Radius using r [SizeExtension].
Radius get r => Radius.elliptical(x.r, y.r);
Radius get dm => Radius.elliptical(x.dm, y.dm);
Radius get dg => Radius.elliptical(x.dg, y.dg);
Radius get w => Radius.elliptical(x.w, y.w);
Radius get h => Radius.elliptical(x.h, y.h);
... ...
name: flutter_screenutil
description: A flutter plugin for adapting screen and font size.Guaranteed to look good on different models
version: 5.8.4
version: 5.9.0-beta
homepage: https://github.com/OpenFlutter/flutter_screenutil
environment:
sdk: ">=2.12.0 <4.0.0"
sdk: ">=2.17.0 <4.0.0"
flutter: ">=3.10.0"
dependencies:
... ...
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'home.test.dart';
void main() {
const smallerDeviceSize = Size(300, 600);
const smallerDeviceData = MediaQueryData(size: smallerDeviceSize);
const biggerDeviceSize = Size(500, 900);
const biggerDeviceData = MediaQueryData(size: biggerDeviceSize);
const uiSize = Size(470, 740);
group('[Test calculations]', () {
test('Test smaller size', () {
ScreenUtil.configure(
data: smallerDeviceData,
designSize: uiSize,
minTextAdapt: true,
splitScreenMode: false,
);
expect(1.w, smallerDeviceSize.width / uiSize.width);
expect(1.w < 1, true);
expect(1.h, smallerDeviceSize.height / uiSize.height);
expect(1.h < 1, true);
});
test('Test bigger size', () {
ScreenUtil.configure(
data: biggerDeviceData,
designSize: uiSize,
minTextAdapt: true,
splitScreenMode: false,
);
expect(1.w, biggerDeviceSize.width / uiSize.width);
expect(1.w > 1, true);
expect(1.h, biggerDeviceSize.height / uiSize.height);
expect(1.h > 1, true);
});
});
group('[Test overflow]', () {
testWidgets('Test overflow width', (tester) async {
await tester.pumpWidget(ScreenUtilInit(
designSize: uiSize,
child: MaterialApp(home: WidgetTest(width: () => uiSize.width.w)),
));
// Wait until all widget rendered
await tester.pumpAndSettle();
// a Text widget must be present
expect(find.text('Test'), findsOneWidget);
});
testWidgets('Test overflow height', (tester) async {
await tester.pumpWidget(ScreenUtilInit(
designSize: uiSize,
child: MaterialApp(home: WidgetTest(height: () => uiSize.height.h)),
));
// Wait until all widget rendered
await tester.pumpAndSettle();
// a Text widget must be present
expect(find.text('Test'), findsOneWidget);
});
});
testWidgets('[Rebuilding]', (tester) async {
final textFieldKey = UniqueKey();
final buildCountNotifier = ValueNotifier(0);
final focusNode = FocusNode();
Finder textField() => find.byKey(textFieldKey);
await tester.pumpWidget(ScreenUtilInit(
designSize: uiSize,
rebuildFactor: RebuildFactors.always,
child: MaterialApp(
home: Scaffold(
body: Builder(
builder: (context) {
buildCountNotifier.value += 1;
assert(uiSize.width.w == MediaQuery.of(context).size.width);
return SizedBox(
width: 1.sw,
child: Column(
children: [
ValueListenableBuilder<int>(
valueListenable: buildCountNotifier,
builder: (_, count, __) => Text('Built count: $count'),
),
TextField(
key: textFieldKey,
focusNode: focusNode,
),
],
),
);
},
),
),
),
));
await tester.pumpAndSettle();
expect(buildCountNotifier.value, 1);
expect(textField(), findsOneWidget);
expect(focusNode.hasFocus, false);
await tester.tap(textField()).then((_) => tester.pumpAndSettle());
expect(textField(), findsOneWidget);
expect(focusNode.hasFocus, true);
expect(buildCountNotifier.value, 1);
// Simulate keyboard
tester.view.viewInsets = FakeViewPadding(bottom: 20);
await tester.pumpAndSettle();
expect(focusNode.hasFocus, true);
expect(buildCountNotifier.value, 1);
});
}
... ...
import 'package:flutter/widgets.dart';
class WidgetTest extends StatelessWidget {
const WidgetTest({
super.key,
this.width = _zero,
this.height = _zero,
});
final double Function() width;
final double Function() height;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (_, c) {
final w = width(), h = height();
if (c.biggest >= Size(w, h)) {
return const Text('Test');
}
throw Error();
},
);
}
static double _zero() => 0;
}
... ...