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.
- Understanding Dual-Screen Hinge and Folding Display Features
- Implementing the
TwoPanewidget 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(),
);
}
}
Comments
Post a Comment