The biggest appeal of Flutter is being able to create apps that can run on multiple devices with just a single codebase. With the stable release of Flutter for the web, the apps you create become even more accessible.
Even though the apps you create will run on all compatible devices, we are faced with the challenge of displaying the optimal UI on a huge variety of screen sizes. That is why it is more important than ever to make your apps responsive.
In this tutorial you will learn how to use the Responsive Framework package to easily make your app UI adjust to different screen sizes.
The Finished App
In this tutorial we are going to transform an existing simplified e-learning app UI from non-responsive to responsive. The finished app will adjust in various ways depending on the breakpoints we define for different screen sizes. The final project will be able to do the following for different breakpoint conditions:
- Scale and resize
- Change the course tiles section from
Column
toRow
and vice versa - Hide or display certain elements of the app bar
- Change the font size value of the page header text
Getting Started
In this tutorial we’ll be working with a starter project which you can grab along with the finished project from the links below.
Once you’ve got the starter project, you can go ahead and add the only external dependency we will be using in this lesson - the Responsive Framework package. We’ll be using version 0.1.4. If you are following this lesson in the future, be mindful of any potential breaking changes when using an updated version of this package.
pubspec.yaml
dependencies:
flutter:
sdk: flutter
responsive_framework: ^0.1.4
Starter Project Overview
The starter project we’re working with has a built-out UI that we’ll transform throughout this lesson to become responsive. The project consists of several files in the lib folder and an assets folder. Here’s the breakdown of all the relevant app files/folders:
- lib/main.dart
Here we’ve got a simpleAppWidget
which returns aMaterialApp
that has theCoursesPage
as thehome
argument. - lib/courses_data.dart
This file contains aCourse
class that we use to provide mock data for the course tiles in the app. - lib/courses_page.dart
This is the file where pretty much the entire UI structure lives. This file is responsible for displaying the only page in our app. It's made up of aScaffold
with anAppBar
that has a title and some action buttons. TheScaffold
body
contains aListView
which displays thePageHeader
widget,Column
withCourseTile
widgets and aSubscribeBlock
widget. - lib/widgets.dart
This file contains some of the custom widgets that are used in the courses_page.dart file to keep our code neater. - assets
The starter project also contains an assets folder which has 3 images used in the app for the header image and course tiles.
Understanding Scaling vs. Resizing
Before we begin, let’s see how the starter project looks at different screen sizes. Run the starter project in a browser. Then, enlarge and shrink the browser window and see how the current UI reacts to the screen changes.
Flutter Default Behavior (Resizing)
When testing the starter project, you’ll notice that some things do change as the screen size changes. For instance, the AppBar
keeps stretching no matter how large the screen gets.
Also, the header image shrinks when the screen is small and enlarges up to a maximum of 800 pixels when the screen size goes up. Once the widgets reach their maximum possible size, they stay at that size no matter how large the screen gets. So, by default Flutter does try to a certain degree to adjust the UI to changes in screen size by resizing the widgets. Let’s understand what’s happening better with some examples:
App bar - The AppBar
has a width of double.infinity
, so it will stretch to fill the available width no matter how large the screen gets.
Header image - In the widgets.dart file we specified that the image we have in the header should have a width of 800 pixels. However, you’ll see that it does get smaller when the screen size decreases, but does not exceed 800 pixels when the screen size increases. This is due to ongoing negotiations between the parent and child widgets regarding how much space the child can take up. The image width changes based on the constraints defined by the parent widget.
You might think that the layout we have now doesn’t look too bad and can be shipped as is with the default resizing behavior. You might be right, as the UI is still usable, but just because something works doesn’t mean we can’t make it better.
Responsive Framework Difference (Scaling)
As discussed above, by default Flutter resizes the widgets on the screen. While this is nice, for certain devices and screen sizes, it would be beneficial if the UI could scale proportionally as well. This is the core offering of the Responsive Framework package.
Let’s think back to the AppBar
and how it behaves when resized. The width of the AppBar
increases to fill the maximum available space, but the height stays the same no matter how large the screen gets. The text and icons inside the AppBar
don’t change either. When the screen size gets really big, this could become a problem in terms of readability and general appearance.
The Responsive Framework package can help us proportionally scale the height of the AppBar
as the width increases. This doesn’t only apply to the AppBar
either. When we activate scaling, all of the widgets on the screen will scale. Making them more suitable for a specific screen size as well as making the text more readable.
Setting Up Responsive Breakpoints
When to Scale and When to Resize?
Now that we understand the difference between scaling and resizing, let’s add scaling behavior to our app. First, though, we need to plan out at which screen sizes our app UI should scale. To do that we need to define breakpoints for the app and specify whether we want our app to scale or resize at those specific breakpoints. If you’re wondering why we wouldn't just have our UI scale continuously, let’s see what happens if we do that.
As you can see, if the app scales continuously, it will quickly get far too big. So, instead, we only want scaling to happen between certain breakpoints where it makes the most sense. For example, our standard UI looks perfect on small screens like cell phones. So, for smaller screens it is better to leave the default, resizing behavior and not add scaling. However, on tablets and very large screens, it may make sense to add scaling behavior. If we set our app to resize on cell phone screens and scale on tablets, it would look something like this.
This is a clip of the finished project. Here the app resizes when the screen width is less than 600px, then from 600px until 800px it scales. So, hopefully, you understand now why we would benefit from scaling in some cases and not in others.
Adding Breakpoints to the App
Knowing what we know now, let’s configure the breakpoints for the app. Head over to the main.dart file, import the Responsive Framework package, and add the following code to the builder
argument of the MaterialApp
.
main.dart
return MaterialApp(
builder: (context, widget) => ResponsiveWrapper.builder(
ClampingScrollWrapper.builder(context, widget!),
breakpoints: const [
ResponsiveBreakpoint.resize(350, name: MOBILE),
ResponsiveBreakpoint.autoScale(600, name: TABLET),
ResponsiveBreakpoint.resize(800, name: DESKTOP),
ResponsiveBreakpoint.autoScale(1700, name: 'XL'),
],
),
...
Let’s discuss what’s happening here.
- To configure the breakpoints and scaling behavior globally, we are returning a
ResponsiveWrapper.builder
from the callback function in thebuilder
argument of theMaterialApp
. - For the positional argument of the
ResponsiveWrapper.builder
we need to provide thewidget
that we get from the callback parameter. We are providing thatwidget
here wrapped in aCalmpingScrollWrapper.builder
. The primary reason for that is to disable the overscroll glow, which is a default effect on Android. You can also use theBouncingScrollWrapper.builder
here if you prefer bouncing over clamping behavior. - Here we also provided a
List
ofResponsiveBreakpoint
objects to thebreakpoints
argument. We usedresize
andautoScale
named constructors depending on which behavior we want for the specific breakpoint. Our app will resize when the screen width is between 350px and 600px, and between 800px and 1700px. The app will scale when the screen width is between 600px and 800px as well as when the screen is larger than 1700px. It is completely up to you which breakpoints you set and the behavior you specify. As a general rule of thumb, though, you will likely want to have some scaling for tablets and very large screens to make sure the UI doesn’t appear too small. You can play around with the different behaviors and breakpoint values to achieve the most suitable effect.
We also specified a name
for every breakpoint. These names can be used throughout the app to reference these breakpoints when configuring other features of the Responsive Framework package.
You’ll also notice that some breakpoints use constants for the names, while the last one uses a String
. These constants are provided to us by the package for easy referencing, but you can also use your own String
values.
Lastly, you can also set up the scaleFactor
argument for the breakpoints if you want your app to be scaled by a custom amount.
.autoScaleDown
and .tag
named constructors. The .autoScaleDown
constructor will have the same behavior as .autoScale
, but it will also be able to scale down. The .tag
constructor can be used when you don’t want to set a specific behavior and just want to create a breakpoint you can reference by name elsewhere in the app.This is everything we are going to do inside of the main.dart file for our app. There are lots of other arguments we haven’t used here that you can customize for the ResponsiveWrapper
. Here are some of the most prominent ones.
- breakpointsLandscape - You can use this argument to specify breakpoints for when a device is in a landscape orientation.
- landscapePlatforms - By default landscape breakpoints will only be active when the app is running on Android, iOS, or Fuchsia. To change this, you can pass in additional platforms in a
List
to thelandscapePlatforms
argument. - minWidth & maxWidth - You can use these arguments to set the maximum and minimum width for the entire app.
- defaultName, defaultScale & defaultScaleFactor - You may have noticed that these arguments are similar to the
ResponsiveBreakpoint
arguments. That’s because they will be used by default for screen sizes which don’t have a set breakpoint. In our app we don’t have adefaultName
set, but you can set one. Since we didn’t specify any custom values for the other two arguments, thedefaultScale
is set tofalse
and thedefaultScaleFactor
is set to 1. We don’t have a breakpoint set before 350px, so before that breakpoint is reached the app’s behavior is determined by these default arguments. So, in our case the app will resize on screens smaller than 350px and not scale. - background & backgroundColor - You can use these arguments to set things like a background image and background color for the app. In our app we aren’t using these and even if we did it wouldn’t be visible. That’s because our
Scaffold
has a white background and it stretches to fill the entire screen.
Run the app in the browser now to see how these changes affect our UI. Try resizing the browser window to see when the app scales and when it resizes.
You’ll notice that when the app switches from scaling to resize, the UI snaps back from scaled to the original size. This will happen when a new breakpoint is reached. The behavior specified for that next breakpoint will start from those dimensions.
You can use this in your favor if, for example, you want the app to scale for a larger breakpoint range. Let’s say we want the app to scale from 600px to 1000px. If we only have one breakpoint for 600px set to scale and one breakpoint for 1000px set to resize, scaling will cause the app to get too large due to such a big range. To mitigate this, we can set another breakpoint at 800px and set it to scale. This will cause the UI to snap back to its original size when the screen width reaches 800px and then scale from there up to the 1000px breakpoint.
Responsive Row/Column
Our app is already more responsive than it was before, but wouldn’t it be nice if the course tile Column
could change to a Row
on larger screens? The Responsive Framework package has a handy widget that can do just that. Head over to the courses_page.dart, import the Responsive Framework package and let’s convert the Column
containing CourseTile
widgets to a ResponsiveRowColumn
widget.
courses_page.dart
...
ResponsiveRowColumn(
rowMainAxisAlignment: MainAxisAlignment.center,
rowPadding: const EdgeInsets.all(30),
columnPadding: const EdgeInsets.all(30),
layout: ResponsiveWrapper.of(context).isSmallerThan(DESKTOP)
? ResponsiveRowColumnType.COLUMN
: ResponsiveRowColumnType.ROW,
children: [
ResponsiveRowColumnItem(
rowFlex: 1,
child: CourseTile(course: courses[0]),
),
ResponsiveRowColumnItem(
rowFlex: 1,
child: CourseTile(course: courses[1]),
),
],
),
...
Let’s go over what we’ve done here step by step.
- First, we are setting the
rowMainAxisAlignment
to center our course tiles when they are displayed in aRow
. - Then, we are using the
rowPadding
andcolumnPadding
arguments to specify padding for both states. - We use a ternary operator to set the
layout
argument dynamically depending on the screen size. We do this by checking if the current width is smaller than theDESKTOP
breakpoint (800px) and if it is, then we set thelayout
argument toResponsiveRowColumnType.COLUMN
. If the current width is instead 800px and larger, the layout will be set toResponsiveRowColumnType.ROW
. - For the
children
argument, we provide aList
of ourCourseTile
widgets wrapped inside of theResponsiveRowColumnItem
widgets. We also set therowFlex
argument for these widgets to 1 to prevent overflow errors.
Go ahead and run the app again. You should now see the column change to a row when the screen size reaches 800px.
Responsive Visibility
There's another neat widget the Responsive Framework package provides us with that can help us conditionally display widgets in our app based on the breakpoint conditions we specify. To demonstrate this, let’s make certain parts of our AppBar
become visible or hidden depending on the screen size. For this app we are going to show the MenuTextButton
widgets when the screen is larger than the TABLET
breakpoint and hide these buttons on smaller screens. When the MenuTextButton
widgets are hidden, we will display a leading menu icon button instead.
First, add a leading IconButton
to the AppBar
. Then, wrap the IconButton
with a ResponsiveVisibility
widget and configure it in the following way.
courses_page.dart
...
leading: ResponsiveVisibility(
hiddenWhen: const [
Condition.largerThan(name: TABLET),
],
child: IconButton(
onPressed: () {},
icon: const Icon(Icons.menu),
),
),
...
The ResponsiveVisibility
widget has several arguments you can configure. Here we are just providing the IconButton
as the child
and a List
with a single condition to the hiddenWhen
argument. We use hiddenWhen
to specify our conditions because by default the visible
argument of this widget is set to true
. So, this way the widget will be visible except when the screen is larger than the TABLET
breakpoint. When creating conditions you can also use .equals
and .smallerThan
constructors instead of the .largerThan
one. What you choose really depends on your personal use case.
Before we see how this looks, let’s wrap our MenuTextButton
action buttons in ResponsiveVisibility
widgets as well.
courses_page.dart
...
actions: [
const ResponsiveVisibility(
visible: false,
visibleWhen: [
Condition.largerThan(name: TABLET),
],
child: MenuTextButton(text: 'Courses'),
),
const ResponsiveVisibility(
visible: false,
visibleWhen: [
Condition.largerThan(name: TABLET),
],
child: MenuTextButton(text: 'About'),
),
...
What’s different here is that we are setting the visible
argument to false
and using visibleWhen
to set our conditions. This makes these buttons hidden by default and only visible when the screen size is larger than the TABLET
breakpoint.
Now, let’s run the app once again and see how it looks. You should now see the AppBar
change based on the conditions we set.
Responsive Values
Our app transformation is almost complete. There is just one more Responsive Framework package feature I would like to show you how to implement. The package provides us with a class we can use to create an object that contains different values depending on the breakpoint conditions we set.
These values can be of any type, but for our example we are going to create a dynamic double
to set a font size for our header text. Switch over to the widgets.dart file and find the PageHeader
widget. There, find the Text
widget that displays the ‘Our Courses’ text and change the current font size to the following.
widgets.dart
...
fontSize: ResponsiveValue(
context,
defaultValue: 60.0,
valueWhen: const [
Condition.smallerThan(
name: MOBILE,
value: 40.0,
),
Condition.largerThan(
name: TABLET,
value: 80.0,
)
],
).value,
...
We are only specifying three arguments for the ResponsiveValue
object here. The context
, defaultValue
, and valueWhen
. The defaultValue
will be used when no conditions are met. The valueWhen
contains a List
of conditions. For these conditions we specify the name
of the breakpoints and the value
we want when they are met.
We can’t simply provide the ResponsiveValue
object to the fontSize
argument, so that is why at the end we are accessing the value
field to retrieve the double
. With this configuration, when the screen size is smaller than the MOBILE
breakpoint, the font size will be 40 and when the screen size is larger than the TABLET
breakpoint it will be 80.
Go ahead and run the app one more time. You should now see the header font size change when the conditions we specified are met.
Other Package Features
We are now all done configuring our app. But before we wrap up this tutorial I would like to mention some of the Responsive Framework package features that we haven’t covered, but you may find worth exploring.
Responsive Constraints
You can use a ResponsiveConstraints
widget to wrap your widgets. It will return a Container
with BoxConstraints
based on the conditions you specify. This Container
will wrap around the widget you provide as the child
for ResponsiveConstraints
.
Responsive GridView
You can use the ResponsiveGridView
provided by the package to create a GridView
with additional grid layout controls. You can check out the package source code for more details.
Conclusion
That’s all for this tutorial! We learned how to utilize the Responsive Framework package to make Flutter apps more optimal on all kinds of screens. All of the features you learned about here can be used in a variety of ways to achieve different looks and effects. The configuration you decide on at the end will vary depending on the app you’re working on. However, no matter what the app is, what you’ve learned here should allow you to customize any project in the way you see fit.
Nice Article! Thanks for the intuitive tutorial. I’ve tried to follow you on twitter but the link point to this same page. Do you mind providing the link?
Nice Article… In this specific case, If I don’t want to scale any one specific screen in the whole app, so at that time what should I use.
Wow, superb blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your site is magnificent, as well as the content!
I loved as much as you will receive carried out right here. The sketch is attractive, your authored material stylish. nonetheless, you command get got an impatience over that you wish be delivering the following. unwell unquestionably come more formerly again since exactly the same nearly a lot often inside case you shield this hike.
hi!,I like your writing so much! share we be in contact more approximately your article on AOL? I need a specialist in this area to resolve my problem. Maybe that is you! Looking ahead to see you.
Thank you for the auspicious writeup. It in fact was a amusement account it. Look advanced to far added agreeable from you! However, how can we communicate?
obviously like your web-site but you need to test the spelling on quite a few of your posts. Several of them are rife with spelling problems and I to find it very troublesome to inform the reality on the other hand I’ll certainly come back again.
Excellent blog here! Also your website loads up very fast! What web host are you using? Can I get your affiliate link to your host? I wish my web site loaded up as quickly as yours lol
I loved as much as you’ll receive carried out right here. The sketch is attractive, your authored material stylish. nonetheless, you command get bought an nervousness over that you wish be delivering the following. unwell unquestionably come more formerly again as exactly the same nearly a lot often inside case you shield this hike.
of course like your website but you have to check the spelling on several of your posts. A number of them are rife with spelling issues and I in finding it very troublesome to inform the reality on the other hand I will certainly come back again.
I just could not leave your web site before suggesting that I really enjoyed the standard information a person supply to your visitors? Is gonna be again steadily in order to check up on new posts.
I’ve read several just right stuff here. Certainly price bookmarking for revisiting. I wonder how a lot effort you place to create this kind of great informative website.
I loved as much as you’ll receive carried out right here. The sketch is tasteful, your authored material stylish. nonetheless, you command get bought an nervousness over that you wish be delivering the following. unwell unquestionably come more formerly again since exactly the same nearly a lot often inside case you shield this hike.
Just wish to say your article is as surprising. The clearness in your post is just cool and i could assume you’re an expert on this subject. Fine with your permission allow me to grab your RSS feed to keep updated with forthcoming post. Thanks a million and please keep up the enjoyable work.
Thank you for the auspicious writeup. It in fact was a amusement account it. Look advanced to more added agreeable from you! By the way, how could we communicate?
Hello, Neat post. There’s an issue together with your site in internet explorer, would check this텶E still is the marketplace chief and a large element of other folks will leave out your magnificent writing due to this problem.
you are truly a just right webmaster. The site loading speed is incredible. It kind of feels that you’re doing any distinctive trick. In addition, The contents are masterwork. you have done a great activity in this matter!
Somebody essentially help to make significantly articles I’d state. This is the first time I frequented your web page and up to now? I surprised with the research you made to make this actual post incredible. Fantastic job!
Hi my loved one! I wish to say that this post is amazing, nice written and include approximately all vital infos. I’d like to peer more posts like this.
Its like you read my mind! You appear to know a lot about this, like you wrote the book in it or something. I think that you could do with some pics to drive the message home a little bit, but instead of that, this is fantastic blog. An excellent read. I will certainly be back.
hot sex great porn gallery
Learn about affiliate marketing Best Affiliate Community
Learn about affiliate marketing Top Affiliate Marketing
Join affiliate marketing community AffRip
Increase your earnings Affiliate Discord
Join affiliate marketing community AffRip
Monetize your website Affiliate Marketing
加入联盟营销社区 最佳联盟社区
I was recommended this website by my cousin I am not sure whether this post is written by him as nobody else know such detailed about my trouble You are amazing Thanks
Empower Your Farm with Bwer Pipes’ Irrigation Solutions: Bwer Pipes offers a comprehensive range of irrigation products designed to empower farmers in Iraq. From efficient sprinkler systems to durable pipes, our solutions are engineered to enhance water distribution, promote crop growth, and maximize yields, ensuring agricultural success. Visit Bwer Pipes
Your blog is a treasure trove of valuable insights and thought-provoking commentary. Your dedication to your craft is evident in every word you write. Keep up the fantastic work!
I simply could not go away your web site prior to suggesting that I really enjoyed the standard info a person supply on your guests Is going to be back incessantly to investigate crosscheck new posts
Hi Neat post Theres an issue together with your web site in internet explorer may test this IE still is the marketplace chief and a good component of people will pass over your fantastic writing due to this problem
Just wish to say your article is as surprising The clearness in your post is just cool and i could assume youre an expert on this subject Fine with your permission allow me to grab your RSS feed to keep updated with forthcoming post Thanks a million and please keep up the enjoyable work
Ive read several just right stuff here Certainly price bookmarking for revisiting I wonder how a lot effort you place to create this kind of great informative website
Vitazen Keto Gummies I very delighted to find this internet site on bing, just what I was searching for as well saved to fav
Obrigado, recentemente estive procurando informações sobre este assunto há algum tempo e a sua é a maior que descobri até agora. Mas e em relação aos resultados financeiros Você tem certeza em relação ao fornecimento
Real Estate I really like reading through a post that can make men and women think. Also, thank you for allowing me to comment!
Techno rozen I’m often to blogging and i really appreciate your content. The article has actually peaks my interest. I’m going to bookmark your web site and maintain checking for brand spanking new information.
Fourweekmba Nice post. I learn something totally new and challenging on websites
Nutra Gears I like the efforts you have put in this, regards for all the great content.
Copper Pipes : Known for durability and resistance to corrosion, copper pipes are commonly used in plumbing. ElitePipe Factory in Iraq supplies reliable copper piping solutions.