├── README.md └── responsive_architecture ├── enums └── device_screen_type.dart ├── index.dart ├── responsive ├── orientation_layout.dart ├── responsive_builder.dart ├── screen_type_layout.dart └── sizing_information.dart └── utils └── ui_utils.dart /README.md: -------------------------------------------------------------------------------- 1 | # responsive-architecture 2 | ultimate responsive ui architecture for flutter applications 3 | 4 | - inspired and copied from [filledstacks](https://www.filledstacks.com/post/building-a-responsive-ui-architecture-in-flutter/) 5 | - watch video [here](https://www.youtube.com/watch?v=udsysUj-X4w) 6 | 7 | ## Abstract example 8 | ``` dart 9 | import 'index.dart'; 10 | 11 | List _children = [ 12 | Text('I'), 13 | Text('love'), 14 | Text('flutter'), 15 | Text('dev'), 16 | Text('zim'), 17 | ]; 18 | 19 | // mobile 20 | Widget _mobilePotrait = Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _children,); 21 | Widget _mobileLandscape = Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _children,); 22 | 23 | // tablet 24 | Widget _tabletPotrait = Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _children,); 25 | Widget _tabletLandscape = Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: _children,); 26 | 27 | // desktop 28 | // (...) 29 | 30 | Widget myResponsiveWidget() { 31 | // return appropriate widget based on device 32 | // screen layout 33 | return ScreenTypeLayout( 34 | mobile: OrientationLayout( 35 | portrait: _mobilePotrait, 36 | landscape: _mobileLandscape, 37 | ), 38 | tablet: OrientationLayout( 39 | portrait: _tabletPotrait, 40 | landscape: _tabletLandscape, 41 | ), 42 | ); 43 | } 44 | ``` 45 | 46 | ### Plugin for testing ui responsiveness 47 | - [device_preview](https://pub.dev/packages/device_preview) plugin 48 | - [device_simulator](https://pub.dev/packages/device_simulator) plugin 49 | 50 | ### Other approaches 51 | - [responsive_widgets](https://pub.dev/packages/responsive_widgets) plugin 52 | - [auto_size_text_field](https://pub.dev/packages/auto_size_text_field) plugin 53 | - [flutter_screenutil](https://pub.dev/packages/flutter_screenutil) plugin 54 | - [responsive_framework](https://pub.dev/packages/responsive_framework) plugin 55 | - [relative_scale](https://pub.dev/packages/relative_scale) plugin 56 | -------------------------------------------------------------------------------- /responsive_architecture/enums/device_screen_type.dart: -------------------------------------------------------------------------------- 1 | // supported devices screen types 2 | enum DeviceScreenType { 3 | Mobile, 4 | Tablet, 5 | Desktop, 6 | } 7 | -------------------------------------------------------------------------------- /responsive_architecture/index.dart: -------------------------------------------------------------------------------- 1 | export 'enums/device_screen_type.dart'; 2 | export 'responsive/orientation_layout.dart'; 3 | export 'responsive/responsive_builder.dart'; 4 | export 'responsive/screen_type_layout.dart'; 5 | export 'responsive/sizing_information.dart'; 6 | export 'utils/ui_utils.dart'; -------------------------------------------------------------------------------- /responsive_architecture/responsive/orientation_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class OrientationLayout extends StatelessWidget { 4 | // pass different widget based on device screen orientation 5 | final Widget landscape; 6 | final Widget portrait; 7 | 8 | const OrientationLayout({ 9 | Key key, 10 | this.landscape, 11 | this.portrait, 12 | }) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | var orientation = MediaQuery.of(context).orientation; 17 | 18 | if(orientation == Orientation.landscape) { 19 | return landscape ?? portrait; 20 | } 21 | 22 | // by default return portrait mode widget 23 | return portrait; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /responsive_architecture/responsive/responsive_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'sizing_information.dart'; 4 | import '../utils/ui_utils.dart'; 5 | 6 | class ResponsiveBuilder extends StatelessWidget { 7 | 8 | final Widget Function( 9 | BuildContext context, 10 | SizingInformation sizingInformation, 11 | ) builder; 12 | 13 | const ResponsiveBuilder({Key key, this.builder}) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return LayoutBuilder(builder: (context, boxConstraints) { 18 | 19 | var mediaQuery = MediaQuery.of(context); 20 | 21 | var sizingInformation = SizingInformation( 22 | deviceScreenType: getDeviceType(mediaQuery), 23 | screenSize: mediaQuery.size, 24 | localWidgetSize: Size(boxConstraints.maxWidth, boxConstraints.maxHeight), 25 | ); 26 | 27 | return builder(context, sizingInformation); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /responsive_architecture/responsive/screen_type_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../enums/device_screen_type.dart'; 4 | import 'responsive_builder.dart'; 5 | 6 | class ScreenTypeLayout extends StatelessWidget { 7 | // pass different widget types based on targetted device 8 | final Widget mobile; 9 | final Widget tablet; 10 | final Widget desktop; 11 | 12 | const ScreenTypeLayout({ 13 | Key key, 14 | this.mobile, 15 | this.tablet, 16 | this.desktop, 17 | }) : super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return ResponsiveBuilder(builder: (_, sizingInformation) { 22 | if(sizingInformation.deviceScreenType == DeviceScreenType.Tablet) { 23 | if(tablet != null) { 24 | 25 | return tablet; 26 | } 27 | } 28 | 29 | if(sizingInformation.deviceScreenType == DeviceScreenType.Desktop) { 30 | if(desktop != null) { 31 | return desktop; 32 | } 33 | } 34 | 35 | return mobile; 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /responsive_architecture/responsive/sizing_information.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../enums/device_screen_type.dart'; 4 | 5 | class SizingInformation { 6 | final DeviceScreenType deviceScreenType; 7 | final Size screenSize; 8 | final Size localWidgetSize; 9 | 10 | SizingInformation({ 11 | this.deviceScreenType, 12 | this.screenSize, 13 | this.localWidgetSize, 14 | }); 15 | 16 | @override 17 | String toString() { 18 | return '[SizingInformation] DeviceType: $deviceScreenType | ScreenSize: $screenSize | LocalWidgetSize: $localWidgetSize'; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /responsive_architecture/utils/ui_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../enums/device_screen_type.dart'; 4 | 5 | DeviceScreenType getDeviceType(MediaQueryData mediaQuery) { 6 | double deviceWidth = mediaQuery.size.shortestSide; 7 | 8 | if (deviceWidth > 950) { 9 | return DeviceScreenType.Desktop; 10 | } 11 | 12 | if (deviceWidth > 600) { 13 | return DeviceScreenType.Tablet; 14 | } 15 | 16 | return DeviceScreenType.Mobile; 17 | } 18 | --------------------------------------------------------------------------------