OpenFlutter / flutter_screenutil

Flutter screen adaptation, font adaptation, get screen information
https://pub.dartlang.org/packages/flutter_screenutil
Apache License 2.0
3.87k stars 493 forks source link

Different design sizes support #469

Closed Turbozanik closed 11 months ago

Turbozanik commented 1 year ago

I needed to support mobile/tablet/desktop web screen sizes with different designs in my app. And I can't do this using this library, as I can set only one design size rigt now.

So I have created my own simplified lib for this case folowing the example of yor library.

I guess it would be really nice to have this functionality in this library. It will shurely help me =).

I would appreciate any feedback on this.

Sharing my code: Lib:

extension UIScaling on num {
  double get sp => AppDimensions.instance.setSp(this);

  double get w => AppDimensions.instance.setWidth(this);

  double get h => AppDimensions.instance.setHeight(this);

  double get r => AppDimensions.instance.radius(this);
}

extension EdgeInsetsExtension on EdgeInsets {
  /// Creates adapt insets using r [SizeExtension].
  EdgeInsets get r => copyWith(
        top: top.r,
        bottom: bottom.r,
        right: right.r,
        left: left.r,
      );
}

extension BorderRaduisExtension on BorderRadius {
  /// Creates adapt BorderRadius using r [SizeExtension].
  BorderRadius get r => copyWith(
        bottomLeft: bottomLeft.r,
        bottomRight: bottomRight.r,
        topLeft: topLeft.r,
        topRight: topRight.r,
      );
}

extension RaduisExtension on Radius {
  /// Creates adapt Radius using r [SizeExtension].
  Radius get r => Radius.elliptical(x.r, y.r);
}

extension BoxConstraintsExtension on BoxConstraints {
  /// Creates adapt BoxConstraints using r [SizeExtension].
  BoxConstraints get r => copyWith(
        maxHeight: maxHeight.r,
        maxWidth: maxWidth.r,
        minHeight: minHeight.r,
        minWidth: minWidth.r,
      );

  /// Creates adapt BoxConstraints using h-w [SizeExtension].
  BoxConstraints get hw => copyWith(
        maxHeight: maxHeight.h,
        maxWidth: maxWidth.w,
        minHeight: minHeight.h,
        minWidth: minWidth.w,
      );
}

class AppDimensions {
  late MediaQueryData mDeviceMediaQueryData;

  late final Size mMobileDefaultSize;
  late final Size mTabletDefaultSize;
  late final Size mDesktopDefaultSize;
  late final Size mWatchDefaultSize;

  AppDimensions._() {
    mDeviceMediaQueryData = MediaQueryData.fromWindow(WidgetsBinding.instance.window);

    mMobileDefaultSize = const Size(360, 820); // width and height in that order, this is logical pixels 1px * mDeviceMediaQueryData.pixelRatio
    mTabletDefaultSize = const Size(768, 1040); // width and height in that order this is logical pixels 1px * mDeviceMediaQueryData.pixelRatio
    mDesktopDefaultSize = const Size(1440, 1024); // width and height in that order, this is straight pixels
    mWatchDefaultSize = const Size(360, 640); // width and height in that order

    _updateScaleFactors();
  }

  static final AppDimensions _instance = AppDimensions._();

  late double _mUITextScaleFactor = 1;
  late double _mUIHeightScaleFactor = 1;
  late double _mUIWidthScaleFactor = 1;

  static AppDimensions get instance => _instance;

  double get mUITextScaleFactor => _mUITextScaleFactor;

  double get mUIHeightScaleFactor => _mUIHeightScaleFactor;

  double get mUIWidthScaleFactor => _mUIWidthScaleFactor;

  void updateDeviceMediaQueryData(MediaQueryData mediaQueryData) {
    mDeviceMediaQueryData = mediaQueryData;
    _updateScaleFactors();
  }

  Size _getDefaultSizeByDeviceType() {
    DeviceScreenType deviceScreenType = getDeviceType(mDeviceMediaQueryData.size);  //ResponsiveBuilder lib
    if (deviceScreenType == DeviceScreenType.mobile) {
      return mMobileDefaultSize;
    }
    if (deviceScreenType == DeviceScreenType.tablet) {
      return mTabletDefaultSize;
    }
    if (deviceScreenType == DeviceScreenType.desktop) {
      return mDesktopDefaultSize;
    }
    if (deviceScreenType == DeviceScreenType.watch) {
      return mWatchDefaultSize;
    }
    return mMobileDefaultSize;
  }

  void _updateScaleFactors() {
    Size defaultSize = _getDefaultSizeByDeviceType();
    DeviceScreenType deviceScreenType = getDeviceType(mDeviceMediaQueryData.size);

    Fimber.e("actual ${mDeviceMediaQueryData.size.width}" +
        " width ${mDeviceMediaQueryData.size.width}" +
        " height ${mDeviceMediaQueryData.size.height}" +
        " pixel ration ${mDeviceMediaQueryData.devicePixelRatio}" +
        " divided${mDeviceMediaQueryData.size.width / defaultSize.width}");

    _mUITextScaleFactor = mDeviceMediaQueryData.size.width / defaultSize.width;
    _mUIHeightScaleFactor = mDeviceMediaQueryData.size.height / defaultSize.height;
    _mUIWidthScaleFactor = mDeviceMediaQueryData.size.width / defaultSize.width;

    Fimber.e("currentSize ${deviceScreenType.toString()}");
    Fimber.e("height factor $_mUIHeightScaleFactor");
    Fimber.e("width factor $_mUIWidthScaleFactor");
    Fimber.e("width ${mDeviceMediaQueryData.size.width.toString()}");
  }

  double setSp(num initialTextSize) {
    return initialTextSize * mUITextScaleFactor;
  }

  double setWidth(num initialSize) {
    return initialSize * mUIWidthScaleFactor;
  }

  double setHeight(num initialSize) {
    return initialSize * mUIHeightScaleFactor;
  }

  double radius(num initialRadius) => initialRadius * min(mUIWidthScaleFactor, mUIHeightScaleFactor);
}

Usage:

class MyApp extends StatelessWidget with WidgetsBindingObserver {
  MyApp({Key? key}) : super(key: key) {
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  didChangeMetrics() {
    Fimber.e(MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width.toString());
    AppDimensions.instance.updateDeviceMediaQueryData(MediaQueryData.fromWindow(WidgetsBinding.instance.window));
  }
.....
}

Padding(
   padding: EdgeInsets.only(
   left: Dimens.size36.w,
   right: Dimens.size24.w,
   )
)
Bravo2x commented 1 year ago

Hi. i faced the same problem, but it is covered by ScreenUtil.init function see example: @override Widget build(BuildContext context) { if (Get.currentRoute.contains(Strings.numberScreen) && !kIsWeb) { ScreenUtil.init(context, designSize: const Size(1080, 1920)); }

you can easely add any condition and modify designSize 
Turbozanik commented 1 year ago

Hi. i faced the same problem, but it is covered by ScreenUtil.init function see example: @OverRide Widget build(BuildContext context) { if (Get.currentRoute.contains(Strings.numberScreen) && !kIsWeb) { ScreenUtil.init(context, designSize: const Size(1080, 1920)); }

you can easely add any condition and modify designSize 

@Bravo2x And it works with window resize for web?

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

dudodv commented 1 year ago

@Turbozanik you can use this lib (https://pub.dev/packages/flutter_flavorizr) to define screen sizes

lizhuoyuan commented 1 year ago

Hi. i faced the same problem, but it is covered by ScreenUtil.init function see example: @OverRide Widget build(BuildContext context) { if (Get.currentRoute.contains(Strings.numberScreen) && !kIsWeb) { ScreenUtil.init(context, designSize: const Size(1080, 1920)); }

you can easely add any condition and modify designSize 

@Bravo2x And it works with window resize for web?

Can be used on web and windows, but the effect depends on whether it meets your expectations

A7MeDG0L0L commented 1 year ago

@Turbozanik how u solve that problem ?

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

RomanSoviak commented 1 year ago

Did you solve the problem?

Turbozanik commented 1 year ago

Hi. i faced the same problem, but it is covered by ScreenUtil.init function see example: @OverRide Widget build(BuildContext context) { if (Get.currentRoute.contains(Strings.numberScreen) && !kIsWeb) { ScreenUtil.init(context, designSize: const Size(1080, 1920)); }

you can easely add any condition and modify designSize 

@Bravo2x And it works with window resize for web?

Can be used on web and windows, but the effect depends on whether it meets your expectations

@RomanSoviak This approach actually works. I have not yet changed my existing project. But I will use this in the next one.

class MyApp extends StatelessWidget with WidgetsBindingObserver+

 @override
  didChangeMetrics() {
  //this is what my lib does
  // Fimber.e(MediaQueryData.fromView(WidgetsBinding.instance.platformDispatcher.views.first).size.width.toString());
  // AppDimensions.instance
  //    .updateDeviceMediaQueryData(MediaQueryData.fromView(WidgetsBinding.instance.platformDispatcher.views.first)); 
        // you can replace it with this:
       if(kWeb && isDesktopSize()) {
             ScreenUtil.init(context, designSize: const Size(1080, 1920));// and it should trigger ScreenUtil internal sizeFacor update.
        } else if(kWeb && isTabletSize()){
            ScreenUtil.init(context, designSize: const Size(720, 1280));// and it should trigger ScreenUtil internal sizeFacor update.
        } else ....
  }
RomanSoviak commented 1 year ago

@Turbozanik thanks a lot for the reply :))

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 30 days with no activity.

charles0122 commented 1 year ago

need once refresh can show right my code:

Widget build(BuildContext context) {
    Size designSize;
    if (Platform.isIOS) {
      // Apple IOS 14 Size
      designSize = Size(390, 844);
    } else if (Platform.isAndroid) {
      // Desktop 设计尺寸
      designSize = Size(360, 690);
    } else if (Platform.isWindows) {
      // Desktop 设计尺寸
      designSize = Size(1440, 1024);
    } else if (Platform.isMacOS) {
      // MacBook Air Size
      designSize = Size(1280, 832);
    } else {
      designSize = Size(360, 690);
    }
    return ScreenUtilInit(
        designSize: designSize,
        // minTextAdapt: true,
        splitScreenMode: true,
        // useInheritedMediaQuery: true,
        builder: (context, child) {
          return RefreshConfiguration(
            footerTriggerDistance: 15,
            dragSpeedRatio: 0.91,
            headerBuilder: () => MaterialClassicHeader(),
            footerBuilder: () => ClassicFooter(),
            enableLoadingWhenNoData: false,
            enableRefreshVibrate: false,
            enableLoadMoreVibrate: false,
            shouldFooterFollowWhenNotFull: (state) {
              // If you want load more with noMoreData state ,may be you should return false
              return false;
            },
            child: GetMaterialApp(
              title: "Arce",
              // home: WelcomePage(),
              // routes: staticRoutes,
              getPages: AppPages.routes,
              initialRoute: AppPages.INITIAL,
              debugShowCheckedModeBanner: false,
              locale: TranslationService.to.locale,
              fallbackLocale: TranslationService.fallbackLocale,
              translations: TranslationService.to.translations,
              theme: ThemeService.to.lightTheme,
              darkTheme: ThemeService.to.darkTheme,
              themeMode: ThemeService.to.themeMode,
              // theme: ThemeService,
              builder: (context, widget) {

                return MediaQuery(
                    // 设置全局文字缩放因子为默认1.0,文字大小不随系统变化而改变
                    data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
                    child: ResponsiveBreakpoints.builder(
                      child: widget!,
                      breakpoints: [
                        const Breakpoint(start: 0, end: 450, name: MOBILE),
                        const Breakpoint(start: 641, end: 1007, name: TABLET),
                        const Breakpoint(start: 1008, end: 1920, name: DESKTOP),
                        const Breakpoint(
                            start: 1921, end: double.infinity, name: '4K'),
                      ],
                    ));
              },
            ),
          );
        });
  }

before refresh image image after: image image

charles0122 commented 1 year ago

sorry, might my code error,i will check it

github-actions[bot] commented 11 months ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 11 months ago

This issue was closed because it has been inactive for 14 days since being marked as stale.