introduce

This is a translation of an article on Medium, with my own understanding and permission from the author.

It’s about adapting to different screens on tablets and phones.

Original address: medium.com/flutter-community/developing-for-multiple-screen-sizes-a nd-orientations-in-flutter-fragments-in-flutter-a4c51b849434

Android solutions to large screens

In Android, we deal with larger screens, such as tablets. We can use the minimum width qualifier to define the size of the layout file name.

Developer.android.com/training/mu…

Fragments in Android are essentially reusable components that can be used on the screen. The Fragment has its own layout file, and Java/Kotlin classes control the Fragment lifecycle. It’s a pretty big job and requires a lot of code to get started.

Next, we will first look at handling screen orientation in a Flutter and then screen size for a Flutter.

Use direction in Flutter

When we use screen orientation, we want to use the full width of the screen and display as much information as possible.

The following example creates a basic profile page in two directions and builds the layout in different directions to maximize the screen width.

How to solve this problem

Conceptually, the solution is similar to Android’s. We will also have two layouts (this is not the layout file in Android because there is no layout file in Flutter), one for vertical and one for horizontal. Then rebuild updates our layout as the orientation of the device changes.

How are direction changes detected

A widget for OrientationBuilder is provided with Flutter. OrientationBuilder can rebuild the layout when the orientation of the device changes.

typedef OrientationWidgetBuilder = Widget Function(BuildContext context, Orientation orientation); class OrientationBuilder extends StatelessWidget { /// Creates an orientation builder. const OrientationBuilder({ Key key, @required this.builder, }) : assert(builder ! = null), super(key: key); /// Builds the widgets below this widget given this widget's orientation. /// A widget's orientation is simply a factor of its width relative to its
  /// height. For example, a [Column] widget will have a landscape orientation
  /// if its width exceeds its height, even though it displays its children in
  /// a vertical array.
  final OrientationWidgetBuilder builder;

  Widget _buildWithConstraints(BuildContext context, BoxConstraints constraints) {
    // If the constraints are fully unbounded (i.e., maxWidth and maxHeight are
    // both infinite), we prefer Orientation.portrait because its more common to
    // scroll vertically then horizontally.
    final Orientation orientation = constraints.maxWidth > constraints.maxHeight ? Orientation.landscape : Orientation.portrait;
    return builder(context, orientation);
  }

  @override
  Widget build(BuildContext context) {
    returnLayoutBuilder(builder: _buildWithConstraints); }}Copy the code

OrientationBuilder has a Builder function to build our layout. The Builder function is called when the orientation of the device changes. There are two values of orientation: orientation. Landscape and orientation. Portrait.

@override Widget build (BuildContext context) {returnScaffold (appBar: appBar (), Body: OrientationBuilder (Builder: Context, orientation) {returnOrientation == orientation. Portrait? _buildVerticalLayout () : _ buildHorizontalLayout (); },),); }Copy the code

In the example above, we check if the screen is in portrait mode and build a portrait layout, otherwise we build a landscape layout for the screen. _buildVerticalLayout () and _buildHorizontalLayout () are the methods written to create the corresponding layout.

Create a larger screen layout in Flutter

When we deal with larger screen sizes, we want the screen to adapt to use the available space on the screen. The most straightforward way to do this is to create two different layouts for the tablet and the phone (the layout here represents the visible part of the screen). However, there is a lot of unnecessary code involved and the code needs to be reused.

So how do we solve this problem?

First, let’s look at its most common use cases.

So let’s talk about master-detail Flow. For applications, you’ll see a common scenario. There’s a list of masters, and then when you click on the list Item, it jumps to another screen that shows the Detail details. In the case of Gmail, for example, we have a list of emails, and when we click on one of them, it opens up a page with details, including the content of the message.

If we used the same layout on a tablet, it would be a pretty big waste of space. So what can we do to solve it? We can have both the master list and the detail view on the same screen because we have enough screen space available.

Let’s take a look at how Android solves this problem. Android creates reusable components called fragments from the main list and details view. Fragments can be defined separately from the screen, simply adding them to the screen instead of repeating two sets of code.

This is where the power of Flutter comes in.

Every widget in Flutter is by nature, reusable.

Every widget in Flutter is like a Fragment.

All we need to do is define two widgets, one for the main list and one for the detailed view. In fact, these are similar fragments.

We just need to check that the device is wide enough to handle the list view and the detail view. If so, we display both widgets on the same screen. If the device is not wide enough to contain both screens, we simply display the main list on the screen, click on the list item and navigate to a separate screen to display the detailed view.

First, we need to check the width of the device to see if we can use a larger layout instead of a smaller one. To get the width, we use MediaQuery to get the width and height in Size in dp.

MediaQuery.of(context).size.width
Copy the code

Let’s switch to the second layout by setting the minimum width to 600dp.

Conclusion:

  1. We created two widgets, one containing the main list and one showing a detailed view
  2. We created two screens, and on the first screen, we checked that the device was wide enough to handle the two widgets
  3. If it’s wide enough, we’ll just add two widgets to the first page. If not, we just add the main list Widget on the first page and, after clicking the list item, navigate to the second screen to display the Widget for the detailed view.

Code implementation

The realization of the code I use their own code to explain, with the original author of the code to achieve the idea and the result is the same. The implementation below is that you have a list of numbers and click to display a detailed view.

ListWidget

// You need to define a callback that determines whether to display the change detail view on the same screen or navigate to a different screen on a smaller screen. typedef Null ItemSelectedCallback(int value); Class ListWidget extends StatelessWidget {ItemSelectedCallback ItemSelectedCallback; ListWidget({@required this.itemSelectedCallback}); @override Widget build(BuildContext context) {return new ListView.builder(
        itemCount: 20,
        itemBuilder: (context, index) {
          return new ListTile(
            title: new Text("$index"), onTap: () {/ / set the click event enclosing itemSelectedCallback (index); }); }); }}Copy the code

DetailWidget

Class DetailWidget extends StatelessWidget {final int data; DetailWidget(this.data); @override Widget build(BuildContext context) {return Scaffold(
      body: Container(
        color: Colors.blue,
        child: new Center(
          child: new Text("Detailed view:$data"),),),); }}Copy the code

Note that these widgets are not screens, just widgets that we will use on the screen.

The main screen

class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { bool isLargeScreen; Var selectValue = 0; // Save the selected content @override Widget build(BuildContext context) {return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new OrientationBuilder(builder: (context, orientation) {
        print("width:${MediaQuery.of(context).size.width}"); // Determine the screen widthif (MediaQuery.of(context).size.width > 600) {
          isLargeScreen = true;
        } else {
          isLargeScreen = false; } // The two widgets are displayed in a Row with an empty Container for a small screen // Expanded for a large screen and detailed viewreturnnew Row( mainAxisSize: MainAxisSize.max, children: <Widget>[ new Expanded(child: new ListWidget( itemSelectedCallback: (value) {// Define the click callback for the list itemif (isLargeScreen) {
                  selectValue = value;
                  setState(() {});
                } else {
                  Navigator.of(context)
                      .push(new MaterialPageRoute(builder: (context) {
                    returnnew DetailWidget(value); })); } }, )), isLargeScreen ? new Expanded(child: new DetailWidget(selectValue)) : new Container() ], ); })); }}Copy the code

This is the main page of the application. There are two variables: selectedValue to store selected list items, and isLargeScreen to indicate whether the screen is large enough.

There is also an OrientatinBuilder wrapped around the outside, so if the phone is rotated to landscape and has enough width to show two widgets, it will be rebuilt this way. (If you don’t need this feature, you can just remove OrientatinBuilder.)

  • The main parts of the code are:
  isLargeScreen
                ? new Expanded(child: new DetailWidget(selectValue))
                : new Container()
Copy the code

If isLargeScreen is true, add a DetailWidget inside of Expanded control. Expanded allows each widget to populate the screen by setting Flex properties.

If isLargeScreen is false, return an empty Container and the Expanded of the ListWidget will automatically fill the screen.

  • The second important part is:
// Define click callbacks for list itemsif (isLargeScreen) {
                  selectValue = value;
                  setState(() {});
                } else {
                  Navigator.of(context)
                      .push(new MaterialPageRoute(builder: (context) {
                    return new DetailWidget(value);
                  }));
                }
Copy the code

Define click callbacks for list items. If the screen is small, we need to navigate to a different page. If the screen is large, you don’t need to navigate to a different screen because the DetailWidget is on that screen, just call setState() to refresh the screen.

We now have a functional application that can accommodate different screen sizes and orientations.

Something more important

  1. If you simply want to have a different layout instead of a fragment-like layout, you can simply write different methods in the Build method to build.
if (MediaQuery.of(context).size.width > 600) {
          isLargeScreen = true;
        } else {
          isLargeScreen = false;
        }
return isLargeScreen? _buildTabletLayout() : _buildMobileLayout();
Copy the code
  1. If you just want to design an application for a tablet, you can’t directly check the width of MediaQurey to determine, but you need to get Size and use it to get the actual width. In landscape mode, width is actually the length of the tablet, and height is actually the width of the tablet.
Size size = MediaQuery.of(context).size;
double width = size.width > size.height ? size.height : size.width;
if(width > 600) {
  // Do something for tablets here
} else {
  // Do something for phones
}
Copy the code
  1. Mandatory somehow screen operation: the need for mandatory landscape or portrait, use SystemChrome. SetPreferredOrientations.
/ / forced vertical screen SystemChrome. SetPreferredOrientations ([DeviceOrientation. PortraitUp, DeviceOrientation. PortraitDown]); / / forced landscape SystemChrome. SetPreferredOrientations ([DeviceOrientation. LandscapeLeft, DeviceOrientation. LandscapeRight]);Copy the code

Making a link

He also followed the original author of the next demo, incidentally added a note, convenient to understand their own.

Own Github link: github.com/LXD31256949…

Original author’s Github link: github.com/deven98/Flu…