A collection of Flutter Widgets that make multi screen user experiences easy to build

Overview

Multi Screen Layout for Flutter

pub package

A collection of Widgets that make multi screen user experiences easy to build

Supported Devices

  • Surface Duo
  • Surface Duo 2
  • Galaxy Z Fold 1 (Flex Mode)
  • Galaxy Z Fold 2 (Flex Mode)
  • Galaxy Z Flip (Flex Mode)
  • LG Wing

If you know of other devices that could support multi screen layouts, please submit a PR and add them to this list.

Install

In your pubspec.yaml

dependencies:
  multi_screen_layout: ^3.1.0

In your app build.gradle

dependencies { 
    implementation "androidx.window:window:1.0.0-rc01"
    implementation 'androidx.window:window-java:1.0.0-rc01'
}

Testing

For testing without access to these physical devices you can use some specific emulators.

  • 6.7 Horizontal Fold-in emulator available in Android Studio
  • 7.6 Fold-in with outer display emulator available in Android Studio
  • 8 Fold-out emulator available in Android Studio
  • Surface Duo Emulator available here.

Layouts

TwoPageLayout

Displays two Widgets, one per screen.

On a Microsoft dual screen device when the app is being spanned across two screens, TwoPageLayout displays both widgets, one per screen. This is designed to help you build Two Page, Dual View, and Companion Pane dual screen app patterns from Microsoft.

On a folding screen device when the display could be utilized to split content areas, TwoPageLayout displays both widgets, one per content area. This is designed to help you build Flex Mode user experiences. You can read more about how when to split content is decided in the AndroidX WindowManager documentation.

On a single screen device, or when the app is only running on a single screen, only child will be displayed.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return TwoPageLayout(
      child: Scaffold(body: Center(child: Text('Hello from page 1!'))),
      secondChild: Scaffold(body: Center(child: Text('Hello from page 2!'))),
    );
  }
}

Surface Duo Example

Two Page 1

Two Page 2

Samsung Z Fold 2 Flex Mode Example

See video here

MasterDetailLayout

Very similar to TwoPageLayout. This layout has better support for having related, "deeper", content in the second page that would usually be accessed by navigating to a new page.

It's common to use this type of layout when you have a list of items that when tapped let you view a detailed view of the item. Email and instant messaging apps are good examples of this.

On a single screen device, or when the app is only running on a single screen, master will display first. When isSelected is true, detail is displayed as a new page on top of master, similar to using Navigator.push.

When displaying on 2 screens, both master and detail display at the same time and no navigation occurs. Even when isSelected is false.

Similar to TwoPageLayout, on a folding screen device when the screen is half opened the screen is treated as a dual screen device.

MasterDetailLayout also handles switching between spanned and non-spanned modes appropriately, so the UI will be the same if you select and then span, or span and then select.

class MasterDetailLayoutExample extends StatefulWidget {
  @override
  _MasterDetailLayoutExampleState createState() =>
      _MasterDetailLayoutExampleState();
}

class _MasterDetailLayoutExampleState extends State<MasterDetailLayoutExample> {
  int selectedItem;

  @override
  Widget build(BuildContext context) {
    return MasterDetailLayout(
      master: EmailList(onItemSelected: (selected) {
        setState(() {
          selectedItem = selected;
        });
      }),
      detail: EmailDetail(itemNumber: selectedItem),
      isSelected: selectedItem != null,
    );
  }
}

Surface Duo Example

MasterDetail

Samsung Z Fold 2 Flex Mode Example

See video here

Direct Data Access

Direct access is for advanced uses cases. The above layouts should be suitable for most apps.

There may be cases where you want to access multi screen information instead of just using the above layout widgets. Here is how to do that.

MultiScreenInfo

MultiScreenInfo is a Widget that lets you access information about the device directly in your Widget tree, it will rebuild when the data changes.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MultiScreenInfo(
        builder: (info) {
          return Column(
            children: <Widget>[
              Text('The below information is from the Surface Duo SDK'),
              Text('isAppSpanned: ${info.surfaceDuoInfoModel.isSpanned}'),
              Text('hingeAngle: ${info.surfaceDuoInfoModel.hingeAngle}'),
            ],
          );
        },
      ),
    );
  }
}

PlatformHandlers

If you need access to information about the device outside of the Widget tree, you can also make platform calls yourself.

SurfaceDuoPlatformHandler

Future getSurfaceDuoInfo() async {
    var hingeAngle = await SurfaceDuoPlatformHandler.getHingeAngle();
    var isDual = await SurfaceDuoPlatformHandler.getIsDual();
    var isSpanned = await SurfaceDuoPlatformHandler.getIsSpanned();
    var nonFunctionalBounds = await SurfaceDuoPlatformHandler.getNonFunctionalBounds();
  }

Extra Documentation

You might also like...

A collection of Animations that aims to improve the user experience for your next flutter project.

A collection of Animations that aims to improve the user experience for your next flutter project.

Flutter Animations A collection of Animations that aims to improve the user experience for your next flutter project. Built by Ezaldeen SAHB I hope th

Dec 24, 2022

Flutter onboarding screen - An example of onboarding screen on Flutter

Flutter onboarding screen - An example of onboarding screen on Flutter

Flutter Onboarding Screen IntroViews is inspired by Paper Onboarding. Thanks to Fluttery. Future Development Pull requests and suggestions welcome. Lo

Aug 3, 2022

A Flutter plugin for changing the Home Screen, Lock Screen (or both) Wallpaper on Android devices.

A Flutter plugin for changing the Home Screen, Lock Screen (or both) Wallpaper on Android devices.

wallpaper_manager A Flutter plugin for changing the Home Screen, Lock Screen (or both) Wallpaper(s) on Android devices. Usage Installation In the pubs

Nov 28, 2022

Flutter screen adaptation, font adaptation, get screen information

Flutter screen adaptation, font adaptation, get screen information

flutter_screenutil A flutter plugin for adapting screen and font size.Let your UI display a reasonable layout on different screen sizes! Note: This pl

Jan 6, 2023

Expense tracker - Build an app in flutter that can record the transaction we make in our daily life

Expense tracker - Build an app in flutter that can record the transaction we make in our daily life

expense_tracker I tried to build an app in flutter that can record the transacti

Nov 6, 2022

Quickly is build as a tool to enhance your Flutter UI development experience and make code easier

Quickly is build as a tool to enhance your Flutter UI development experience and make code easier. It is highly inspired by Bootstrap and Tailwind CSS. It also provide lots of extension methods on String, List and Map.

Oct 24, 2022

Hyakunin-isshu - This library has been developed to make it easy to use Japanese Hyakunin Isshu in Dart and Flutter applications

Hyakunin-isshu - This library has been developed to make it easy to use Japanese Hyakunin Isshu in Dart and Flutter applications

1. About 1.1. What Is Hyakunin Isshu? 1.2. Introduction 1.2.1. Install Library 1

Jan 11, 2022

Easy-to-make native ads in flutter using AdMOB SDK.

native_admob_flutter Easy-to-make ads in Flutter with Google's AdMob SDK. English | Português Get started To get started with Native Ads for Flutter,

Dec 12, 2022

A smartphone application called Easy Job goal is to make easier for businesses to find people who meet their standards as well as for job seekers to search for and choose from available positions .

Easy_Jobs 19SW54(MAD-Project) A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to ge

Nov 6, 2022
Comments
  • Z fold 2 build

    Z fold 2 build

    While running apk with 1.0.1 lib it crashes with missing implementation medhod getInfoModel(). Is there any other dependency which should be included for galaxy fold device?

    image

    opened by kordek212 16
  • MultiScreenInfo.surfaceDuoInfoModel.isSpanned = false while SurfaceDuoPlatformHandler.getIsSpanned() = true

    MultiScreenInfo.surfaceDuoInfoModel.isSpanned = false while SurfaceDuoPlatformHandler.getIsSpanned() = true

    Hello, I made this simple example. When I debugged this application everything worked fine. As soon as I wanted to deploy it in release mode an error ("java.lang.AbstractMethodError: abstract method "void androidx.window.sidecar.SidecarInterface$SidecarCallback.onDeviceStateChanged(androidx.window.sidecar.SidecarDeviceState)"") came which I resolved by adding dependencies { implementation "androidx.window:window:1.0.0-beta04" } to the build.gradle file. Now the app runs but it seems to not think the device is not Spanned when it is spanned so I always just see the first child from TwoPageLayout.

    MultiScreenInfo(builder: (info) {
            print(info.surfaceDuoInfoModel.isSpanned);
    }); **returns false;**
    
    SurfaceDuoPlatformHandler.getIsSpanned().then((value) => print(value)); **returns true**
    
    Code: https://www.toptal.com/developers/hastebin/isukovusaj.java
    or
    `import 'dart:async';
    
    import 'package:flutter/material.dart';
    import 'package:multi_screen_layout/multi_screen_layout.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Spikeball Counter',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: MultiScreenInfo(builder: (info) {
            print(info.surfaceDuoInfoModel.isSpanned);
            SurfaceDuoPlatformHandler.getIsSpanned().then((value) => print(value));
            return TwoPageLayout(child: const MyHomePage(color: Colors.blue), secondChild: MyHomePage(color: Colors.red.shade400));}),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key? key, required this.color}) : super(key: key);
      final Color color;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int _counter = 0;
      bool _winner = false;
      Color bg = Colors.white;
    
      @override
      void initState() {
        super.initState();
        bg = widget.color;
      }
    
      void _incrementCounter() {
        setState(() {
          _counter++;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: GestureDetector(
              onTap: () {
                if(_winner) return;
                _incrementCounter();
                if(_counter == 25) {
                  _winner = true;
                  Timer.periodic(const Duration(milliseconds: 500), (Timer timer) {
                      if (_counter != 25) {
                        setState(() {
                          _winner = false;
                          timer.cancel();
                          bg = widget.color;
                        });
                      } else {
                        setState(() {
                          bg = (bg == widget.color ? Colors.white : widget.color);
                        });
                      }
                    },
                  );
                }
              },
              child: Container(color: bg, child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'Team ' + (widget.color == Colors.red.shade400 ? 'Rot' : 'Blau'), style: TextStyle(fontSize: 20, color: (widget.color == Colors.red.shade400 ? Colors.red[900] : Colors.blue[900])),
                ),
                Text(
                  '$_counter',
                  style: TextStyle(fontSize: 280, color: (widget.color == Colors.red.shade400 ? Colors.red[900] : Colors.blue[900])),
                ),
              ],
            )),
          )),
          floatingActionButton: Row(mainAxisAlignment: MainAxisAlignment.end, children: [
            IconButton(onPressed: () {setState(() {
              if(_counter == 0) return;
            _winner = false;
            _counter -= 1;
          });}, icon: Icon(Icons.exposure_minus_1_rounded)),
            IconButton(onPressed: () {setState(() {
            _winner = false;
            _counter = 0;
          });}, icon: Icon(Icons.refresh_rounded))],),
        );
      }
    }
    
    opened by kimonneuhoff 10
  • Add a way to know when an app has been spanned from a particular widget

    Add a way to know when an app has been spanned from a particular widget

    It would be helpful to have a way to know if an app has been spanned from a particular widget. For example, let's say we have a list of items as the child for our TwoPageLayout, and a DetailView widget representing the contents of an item in the list. When not spanned, you'll see the list of items, and tapping on an item brings you into the DetailView to see the contents for that item, like so:

    Screenshot_1599158391 Screenshot_1599158393

    When spanned, you'll see the list on the left and the DetailView on the right, like so:

    Screenshot_1599158384

    This is all fine, except when spanning from within the DetailView. So when not spanned, you tap on an item, and from the DetailView you span the app. You'd expect to see the list on the left again, right? What actually happens is that the DetailView will span across both screens:

    Screenshot_1599158398

    It would therefore be useful to be able to tell when we have spanned from a particular widget.

    opened by GroovinChip 9
  • Window manager alpha 6

    Window manager alpha 6

    • Testing docs
    • Android version bumps
      • Target SDK
      • Compile SDK
      • Kotlin version
      • Gradle
      • Gson
      • Window Manager
    • General refactor based on Window Manager changes
    opened by MisterJimson 0
Owner
Jason Rai
Mobile dev from Toronto
Jason Rai
Easily build your Widgets, Avoid parenthesis nesting, easy to build UI, A little like swift-ui.

tenon_mortise Easily build your Widgets, Avoid parenthesis nesting, easy to build UI, A little like swift-ui. Getting Started Usage To use this plugin

JieLiu 4 Dec 15, 2022
Know where to go safely. Describe your experiences and rate places.

Is It Safe? ?? Índice Sobre Showcase Features Como eu posso rodar o projeto? Ferramentas Instalação Run Suporte Como posso contribuir? Autores Info ??

Is It Safe? 0 Sep 19, 2022
A collection of templates to make new Flutter components using the Mason build tool.

Mason Flutter Templates A collection of templates to make new Flutter components using the Mason build tool. Setup in Project To use these templates i

Marcus Twichel 5 Jun 21, 2022
Doctor Consultation App in Flutter containing splash screen on boarding screen Routing state management Dash board Bottom navigation Decorated Drawer and Doctors Screen in the last.

Online doctor Consultation App UI in Flutter Doctor Consultation App UI in Flutter Visit Website Features State Management Navigation Bar Responsive D

Habib ullah 14 Jan 1, 2023
Android test task master - Create PIN code screen, authentication by PIN code screen and menu screen

Here is described test tasks for a android dev. Need to implement three screens:

null 3 Oct 4, 2022
Multi Translator build with Flutter, It developed with DDD (Domain Driven Design) principles.

Multi Translator App An app utilizes to translate any text to multiple languages. Features Localization Multiple Translation Single Translation Deep L

Selim Üstel 7 Dec 27, 2022
Learn how to build a multi-step form flow and how to use bloc to effectively isolate the presentation layer from the business logic layer.

Multi-page Form Flow Learn how to build a multi-step form flow and how to use bloc to effectively isolate the presentation layer from the business log

Sandip Pramanik 15 Dec 19, 2022
Most popular and easy to use open source UI library with 1000+ Widgets to build flutter app.

GetWidget is a 100% free Flutter open-source UI Kit library built with Flutter SDK to make Flutter development easier and more joyful than ever. GetWi

Ionicfirebaseapp 3.7k Jan 1, 2023
Most popular and easy to use open source UI library with 1000+ Widgets to build flutter app.

GetWidget is a 100% free Flutter open-source UI Kit library built with Flutter SDK to make Flutter development easier and more joyful than ever. GetWi

Ionicfirebaseapp 3.7k Jan 3, 2023
Flutter-Apps-Collection: a collection of apps made in flutter for learning purpose

Flutter-Apps-Collection This is a repository of a collection of apps made in flutter for learning purpose Some Screenshots . . . Apps build in Flutter

Himanshu Singh 96 May 27, 2022