You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
samples/platform_design/lib/main.dart

197 lines
6.3 KiB

// Copyright 2020 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'news_tab.dart';
import 'profile_tab.dart';
import 'settings_tab.dart';
import 'songs_tab.dart';
import 'widgets.dart';
void main() => runApp(const MyAdaptingApp());
class MyAdaptingApp extends StatelessWidget {
const MyAdaptingApp({super.key});
@override
Widget build(context) {
// Either Material or Cupertino widgets work in either Material or Cupertino
// Apps.
return MaterialApp(
title: 'Adaptive Music App',
theme: ThemeData(
// Use the green theme for Material widgets.
primarySwatch: Colors.green,
useMaterial3: true,
),
darkTheme: ThemeData.dark(),
builder: (context, child) {
return CupertinoTheme(
// Instead of letting Cupertino widgets auto-adapt to the Material
// theme (which is green), this app will use a different theme
// for Cupertino (which is blue by default).
data: const CupertinoThemeData(),
child: Material(child: child),
);
},
home: const PlatformAdaptingHomePage(),
);
}
}
// Shows a different type of scaffold depending on the platform.
//
// This file has the most amount of non-sharable code since it behaves the most
// differently between the platforms.
//
// These differences are also subjective and have more than one 'right' answer
// depending on the app and content.
class PlatformAdaptingHomePage extends StatefulWidget {
const PlatformAdaptingHomePage({super.key});
@override
State<PlatformAdaptingHomePage> createState() =>
_PlatformAdaptingHomePageState();
}
class _PlatformAdaptingHomePageState extends State<PlatformAdaptingHomePage> {
// This app keeps a global key for the songs tab because it owns a bunch of
// data. Since changing platform re-parents those tabs into different
// scaffolds, keeping a global key to it lets this app keep that tab's data as
// the platform toggles.
//
// This isn't needed for apps that doesn't toggle platforms while running.
final songsTabKey = GlobalKey();
// In Material, this app uses the hamburger menu paradigm and flatly lists
// all 4 possible tabs. This drawer is injected into the songs tab which is
// actually building the scaffold around the drawer.
Widget _buildAndroidHomePage(BuildContext context) {
return SongsTab(
key: songsTabKey,
androidDrawer: _AndroidDrawer(),
);
}
// On iOS, the app uses a bottom tab paradigm. Here, each tab view sits inside
// a tab in the tab scaffold. The tab scaffold also positions the tab bar
// in a row at the bottom.
//
// An important thing to note is that while a Material Drawer can display a
// large number of items, a tab bar cannot. To illustrate one way of adjusting
// for this, the app folds its fourth tab (the settings page) into the
// third tab. This is a common pattern on iOS.
Widget _buildIosHomePage(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: const [
BottomNavigationBarItem(
label: SongsTab.title,
icon: SongsTab.iosIcon,
),
BottomNavigationBarItem(
label: NewsTab.title,
icon: NewsTab.iosIcon,
),
BottomNavigationBarItem(
label: ProfileTab.title,
icon: ProfileTab.iosIcon,
),
],
),
tabBuilder: (context, index) {
assert(index <= 2 && index >= 0, 'Unexpected tab index: $index');
return switch (index) {
0 => CupertinoTabView(
defaultTitle: SongsTab.title,
builder: (context) => SongsTab(key: songsTabKey),
),
1 => CupertinoTabView(
defaultTitle: NewsTab.title,
builder: (context) => const NewsTab(),
),
2 => CupertinoTabView(
defaultTitle: ProfileTab.title,
builder: (context) => const ProfileTab(),
),
_ => const SizedBox.shrink(),
};
},
);
}
@override
Widget build(context) {
return PlatformWidget(
androidBuilder: _buildAndroidHomePage,
iosBuilder: _buildIosHomePage,
);
}
}
class _AndroidDrawer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
DrawerHeader(
decoration: const BoxDecoration(color: Colors.green),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Icon(
Icons.account_circle,
color: Colors.green.shade800,
size: 96,
),
),
),
ListTile(
leading: SongsTab.androidIcon,
title: const Text(SongsTab.title),
onTap: () {
Navigator.pop(context);
},
),
ListTile(
leading: NewsTab.androidIcon,
title: const Text(NewsTab.title),
onTap: () {
Navigator.pop(context);
Navigator.push<void>(context,
MaterialPageRoute(builder: (context) => const NewsTab()));
},
),
ListTile(
leading: ProfileTab.androidIcon,
title: const Text(ProfileTab.title),
onTap: () {
Navigator.pop(context);
Navigator.push<void>(context,
MaterialPageRoute(builder: (context) => const ProfileTab()));
},
),
// Long drawer contents are often segmented.
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Divider(),
),
ListTile(
leading: SettingsTab.androidIcon,
title: const Text(SettingsTab.title),
onTap: () {
Navigator.pop(context);
Navigator.push<void>(context,
MaterialPageRoute(builder: (context) => const SettingsTab()));
},
),
],
),
);
}
}