A responsive library for Flutter that
- is easy to use and easy to read
- makes your app look great on all devices
- makes your app more readable
- makes your app more maintainable
Welcome to Super Responsive!!!
Getting started is very easy.
First, wrap your app in a Super Responsive widget.
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SuperResponsive(
breakPoints: BreakPoints(
// you can add up to 6 breakpoints
// first and second are required!
first: 1200,
second: 900,
third: 600,
),
// you can create custom values that are going
// to be available to all your widgets.
//
// See Extensions and Responsive Value to find out more
customValues: (context) => [
"title": context.responsiveValue(25, 50),
"subtitle": context.responsiveValue(13, 20),
"any_other_value": 100,
],
child: MaterialApp(
title: 'Super Responsive App',
initialRoute: '/',
),
);
}
}
// Num extensions
100.max(99) // clamps the value with a maximum value of 99
100.min(1) // clamps the value with a minimum value of 1
100.per(50) // return 50% of 100
// it can also be made like 100*0.5 but giving
// a value between 0-100 can be better in other cases
// BoxConstraints extensions
constraints.perWidth(50) // return 50% of maxWidth
constraints.perHeight(10) // return 10% of maxHeight
// BuildContext extensions
context.breakpoints // breakpoints from the closest SuperResponsive widget
context.currentBreakpoint // it will retrun the current breakpoint
context.mediaQueryWidth // as in MediaQuery.of(context).size.width
context.mediaQueryHeight // as in MediaQuery.of(context).size.height
↓
↓
↓
↓
↓ More BuildContext extensions
In order to get a responsive design, we should have a responsive value. This value can be withing any range that we specify, For example:
If we want a container to have a width between 500 and 100, meaning the container will have a width of 500 when our screen width will be equal or greater to our first breakpoint, and it will have a width of 100 when our screen width will be equal or less than our last breakpoint (this one is going to be the last break point we specified).
...
/// in a more graphical way
/// breakpoints extremes -> [600, 1200]
/// our value's range -> [100, 500]
/// when screen width == 800 in the range of [600, 1200],
/// this value will be mapped to the range of [100, 500]
/// and it will return the value = 233.333
/// all this calculation is made by the (mapValue) function,
/// that can also be used by you in any way you want to.
@override
Widget build(BuildContext context) {
return Container(
// you can also use context.responsiveInverseValue(100, 500)
width: context.responsiveValue(100, 500),
height: 300,
color: Colors.red,
);
}
...
In case you don't want to calculate this value in that way and want to assign
specific values depending on the current breakpoint, you can use the when
method inside the Breakpoints
class.
...
@override
Widget build(BuildContext context) {
return Container(
// context.breakpoints will return the breakpoints
// of the closest SuperResponsive widget in the widget tree
width: context.breakpoints.when(
// the current screen width
// you could also use context.whenBreakpoints() which will
// use as maxWidth the screen width automatically
maxWidth: context.mediaQueryWidth,
first: (breakpoint) => breakpoint*0.5, // 50% of the breakpoint
second: (_) => 300, // 300px
third: (_) => context.responsiveValue(100, 300), // or a responsive value
fourth: (_) => 50, // we don't have a fourth breakpoint in our SuperResponsive widget,
// so it will return the last valid case, in this case third: (_) => context.responsiveValue(100, 300)
),
height: 300,
color: Colors.red,
);
}
...
There is also the possibility to use a custom responsive value. Which will calculate the value based on the new breakpoint range that you specify and the value range.
...
@override
Widget build(BuildContext context) {
return Container(
width: context.customReponsiveValue(
// you can use some of your breakpoints, or any
// other value that you want
breakpointsRange: (breakPoints) =>
Range(breakpoints.second, breakpoints.first),
valueRange: Range(100, 500),
),
height: 300,
color: Colors.red,
);
}
...
...
@override
Widget build(BuildContext context) {
return ResponsiveWidget(
// The amount of widgets must be equal to the amount
// of breakpoints we have specified in our SuperResponsive widget
children: [
WidgetLarge(),
WidgetMedium(),
WidgetSmall(),
],
// ! we can also specify new breakpoints for only this widget
// ! this will override the breakpoints of the SuperResponsive widget
// ! and the amount of children must be equal to these new breakpoints
// breakpoints: BreakPoints(
// first: 1000,
// second: 800,
// third: 500,
// ),
);
}
...
...
@override
Widget build(BuildContext context) {
return ResponsiveText(
text: "Super Responsive",
fontSizeRange: Range(20, 30),
// ! we can also specify custom breakpoints for only this widget
// ! this means that the result of the responsive value set
// ! for the font size, will be set to the closest value of the
// ! textBreakpoints. For example, if the value should be
// ! 17, it will be set to 17 because it is the closest value from the
// ! set of textBreakpoints
// textBreakpoints: [
// 30,
// 15,
// 20,
// ]
);
}
...
...
@override
Widget build(BuildContext context) {
// ! it'll return a SizedBox square with dimension equal
// ! to the responsive value of range [100, 300]
return ResponsiveGap(100, 300);
// ! you can also create an "INVERSE" gap, which will
// ! have as a dimension the inverse value. For example,
// ! if the value is 300 and the range is [100, 300],
// ! it will have a final value of 100
return ResponsiveGap(100, 300, reversed: true);
}
...
With this widget you will be able to write complex layouts and make them easier to read, understand and maintain.
The concept is very simple, specify how many layouts you want, the children that will be available for those layouts , your breakpoints(which most of the time are your SuperResponsive breakpoints) and then your layouts.
...
@override
Widget build(BuildContext context) {
return ResponsiveLayout(
// when set to true it will use the available space of the parent
// widget in order to determine it breakpoint and later
// its layout
useBoxConstraints: true,
layoutCount: 3,
children: [
Widget0(),
Widget1(),
Widget2(),
Widget3(),
Widget4(),
],
// ! you can use any breakpoints you want
breakpoints: (breakpoints) => breakpoints,
layouts: (child) => [
// ! Some complex layout with multiple Rows and Columns
// ! Or any other widget that you want
Column(
children: [
// ! the function child() will return the child
// ! of that specific index => child(0) == Widget0()
Row(children:[child(1), child(0)]).expanded(),
// ! .expanded({int flex}) is an extension on Widget
// ! it can be used if you want to wrap your widget inside
// ! an Expanded widget, it has been mainly created to make your
// ! layout more readable and to be used by Columns or Rows
Row(children:[child(2), child(2)]).expanded(flex: 2),
// ! there is also the extension .flexible({int flex, FlexFit fit})
child(3).flexible(flex: 2),
]
),
// ! some very simple layout
Row(children:[child(3), child(3)]),
// ! or just return one of your children
child(4),
]
);
}
...
DO NOT USE .expanded() and .flexible() on a widget that is already wrapped inside an Expanded or Flexible widget.
Doing this will cause unexpected behavior and very ugly errors!!!.
(a bit empty for the moment)
- Add examples
- Add tests