import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; typedef ItemWidgetBuilder = Widget Function( BuildContext context, dynamic item, bool tablet); typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index); class CupertinoMasterDetailController extends StatelessWidget { final ItemWidgetBuilder detailBuilder; final IndexedWidgetBuilder itemBuilder; final Widget? onNull, onEmpty; final PreferredSizeWidget? appBar; final dynamic selectedItem; final ValueChanged itemSelected; final List items; CupertinoMasterDetailController({ required this.detailBuilder, required this.selectedItem, required this.itemSelected, required this.itemBuilder, required this.items, this.appBar, this.onNull, this.onEmpty, }); Widget _buildMobileLayout(BuildContext context) { return Scaffold( body: CupertinoPageScaffold( child: CustomScrollView( semanticChildCount: items.length, slivers: [ CupertinoSliverNavigationBar( largeTitle: Text("Presidents"), ), _ItemListing( onEmpty: onEmpty, onNull: onNull, itemBuilder: itemBuilder, items: items, selectedItem: selectedItem, itemSelectedCallback: (item) { Navigator.push( context, MaterialPageRoute( builder: (context) => detailBuilder(context, item, false)), ); }, ), ]))); } Widget _buildTabletLayout(BuildContext context) { return OrientationBuilder(builder: (context, orientation) { return Row( children: [ Flexible( flex: orientation == Orientation.landscape ? 1 : 2, child: Scaffold( appBar: appBar, body: CustomScrollView(slivers: [ _ItemListing( onEmpty: onEmpty, onNull: onNull, itemBuilder: itemBuilder, items: items, selectedItem: selectedItem, itemSelectedCallback: itemSelected, ), ])), ), Container( width: 1.0, color: Colors.grey[300], ), Flexible( flex: 3, child: detailBuilder(context, selectedItem, true), ), ], ); }); } @override Widget build(BuildContext context) { var shortestSide = MediaQuery.of(context).size.shortestSide; final bool useMobileLayout = shortestSide < 600; if (useMobileLayout) { return _buildMobileLayout(context); } return _buildTabletLayout(context); } } class _ItemListing extends StatelessWidget { _ItemListing({ required this.itemSelectedCallback, this.selectedItem, this.itemBuilder, this.items, this.onNull, this.onEmpty, }); final ValueChanged itemSelectedCallback; final dynamic selectedItem; final IndexedWidgetBuilder? itemBuilder; final List? items; final Widget? onNull, onEmpty; @override Widget build(BuildContext context) { if (items == null) { return onNull ?? Center(child: CircularProgressIndicator()); } if (items!.isEmpty) { return onEmpty ?? Center(child: Text("No Items Found")); } return SliverList( delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { final _child = itemBuilder!(context, index); return GestureDetector( onTap: () => itemSelectedCallback(items![index]), child: _child, ); }, childCount: items!.length, ), ); } }