Building Dual-Screen & Foldable ListView Apps in Flutter using TwoPane

Flutter Tutorial

Building Dual-Screen & Foldable ListView Apps in Flutter using TwoPane

Learn how to adapt your Flutter layout for dual-screen and foldable devices using Microsoft's dual_screen package and Flutter's TwoPane widget.

Dual-screen and foldable mobile devices offer unique screen real estate. Instead of showing a single list view that navigates to a new page, dual-screen layouts allow developers to display a Master-Detail view side-by-side when unfolded or on dual screens, and fall back to a single page view on standard phones.

What You Will Learn:
  • Understanding Dual-Screen Hinge and Folding Display Features
  • Implementing the TwoPane widget for responsive multi-screen layouts
  • Handling Master-Detail state selection dynamically
  • Material 3 visual layout standards for adaptive design

1. Step 1: Add Dependencies

Add the official dual_screen package to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  dual_screen: ^1.0.4

2. Step 2: Create Data Model

Create a simple item model in lib/models/item_model.dart:

class ItemModel {
  final int id;
  final String title;
  final String description;

  const ItemModel({
    required this.id,
    required this.title,
    required this.description,
  });
}

final List<ItemModel> sampleItems = List.generate(
  10,
  (index) => ItemModel(
    id: index + 1,
    title: 'Item ${index + 1}',
    description: 'This is the detailed view content for Item ${index + 1}.',
  ),
);

3. Step 3: Create TwoPane Dual-Screen Layout

Create lib/screens/dual_screen_home.dart. The TwoPane widget automatically handles splitting Pane 1 (Master List) and Pane 2 (Detail View) when a physical hinge or fold is detected, or when screen width allows:

import 'package:flutter/material.dart';
import 'package:dual_screen/dual_screen.dart';
import '../models/item_model.dart';

class DualScreenHome extends StatefulWidget {
  const DualScreenHome({super.key});

  @override
  State<DualScreenHome> createState() => _DualScreenHomeState();
}

class _DualScreenHomeState extends State<DualScreenHome> {
  ItemModel? _selectedItem;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dual-Screen Layout'),
        centerTitle: true,
      ),
      body: TwoPane(
        panePriority: _selectedItem == null ? TwoPanePriority.pane1 : TwoPanePriority.both,
        pane1: _buildListPane(),
        pane2: _buildDetailPane(),
      ),
    );
  }

  Widget _buildListPane() {
    return ListView.builder(
      itemCount: sampleItems.length,
      itemBuilder: (context, index) {
        final item = sampleItems[index];
        final isSelected = _selectedItem?.id == item.id;
        return ListTile(
          selected: isSelected,
          title: Text(item.title),
          subtitle: Text(item.description, maxLines: 1, overflow: TextOverflow.ellipsis),
          trailing: const Icon(Icons.chevron_right),
          onTap: () {
            setState(() {
              _selectedItem = item;
            });
          },
        );
      },
    );
  }

  Widget _buildDetailPane() {
    if (_selectedItem == null) {
      return const Center(
        child: Text('Select an item from the list to view details.'),
      );
    }

    return Container(
      padding: const EdgeInsets.all(24.0),
      color: Theme.of(context).colorScheme.surfaceContainerLow,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            _selectedItem!.title,
            style: Theme.of(context).textTheme.headlineMedium,
          ),
          const SizedBox(height: 16),
          Text(
            _selectedItem!.description,
            style: Theme.of(context).textTheme.bodyLarge,
          ),
        ],
      ),
    );
  }
}

4. Step 4: Application Entry Point

Setup your entry point in lib/main.dart:

import 'package:flutter/material.dart';
import 'screens/dual_screen_home.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.blue,
      ),
      home: const DualScreenHome(),
    );
  }
}

Conclusion

By integrating Flutter's TwoPane widget, your app seamlessly adapts between single-screen phones, dual-screen foldables, and large screen displays, ensuring an optimized Master-Detail user experience.

Comments