mirror of
https://github.com/flutter/samples.git
synced 2026-05-22 06:58:41 +00:00
Add a Material/Cupertino adaptive application example (#69)
This commit is contained in:
168
platform_design/lib/songs_tab.dart
Normal file
168
platform_design/lib/songs_tab.dart
Normal file
@@ -0,0 +1,168 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'song_detail_tab.dart';
|
||||
import 'utils.dart';
|
||||
import 'widgets.dart';
|
||||
|
||||
class SongsTab extends StatefulWidget {
|
||||
static const title = 'Songs';
|
||||
static const androidIcon = Icon(Icons.music_note);
|
||||
static const iosIcon = Icon(CupertinoIcons.music_note);
|
||||
|
||||
const SongsTab({ Key key, this.androidDrawer }) : super(key: key);
|
||||
|
||||
final Widget androidDrawer;
|
||||
|
||||
@override
|
||||
_SongsTabState createState() => _SongsTabState();
|
||||
}
|
||||
|
||||
class _SongsTabState extends State<SongsTab> {
|
||||
static const _itemsLength = 50;
|
||||
|
||||
final _androidRefreshKey = GlobalKey<RefreshIndicatorState>();
|
||||
|
||||
List<MaterialColor> colors;
|
||||
List<String> songNames;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_setData();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _setData() {
|
||||
colors = getRandomColors(_itemsLength);
|
||||
songNames = getRandomNames(_itemsLength);
|
||||
}
|
||||
|
||||
Future<void> _refreshData() {
|
||||
return Future.delayed(
|
||||
// This is just an arbitrary delay that simulates some network activity.
|
||||
const Duration(seconds: 2),
|
||||
() => setState(() => _setData()),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _listBuilder(context, index) {
|
||||
if (index >= _itemsLength)
|
||||
return null;
|
||||
|
||||
// Show a slightly different color palette. Show poppy-ier colors on iOS
|
||||
// due to lighter contrasting bars and tone it down on Android.
|
||||
final color = defaultTargetPlatform == TargetPlatform.iOS
|
||||
? colors[index]
|
||||
: colors[index].shade400;
|
||||
|
||||
return SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Hero(
|
||||
tag: index,
|
||||
child: HeroAnimatingSongCard(
|
||||
song: songNames[index],
|
||||
color: color,
|
||||
heroAnimation: AlwaysStoppedAnimation(0),
|
||||
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => SongDetailTab(
|
||||
id: index,
|
||||
song: songNames[index],
|
||||
color: color,
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _togglePlatform() {
|
||||
TargetPlatform _getOppositePlatform() {
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
return TargetPlatform.android;
|
||||
} else {
|
||||
return TargetPlatform.iOS;
|
||||
}
|
||||
}
|
||||
|
||||
debugDefaultTargetPlatformOverride = _getOppositePlatform();
|
||||
// This rebuilds the application. This should obviously never be
|
||||
// done in a real app but it's done here since this app
|
||||
// unrealistically toggles the current platform for demonstration
|
||||
// purposes.
|
||||
WidgetsBinding.instance.reassembleApplication();
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Non-shared code below because:
|
||||
// - Android and iOS have different scaffolds
|
||||
// - There are differenc items in the app bar / nav bar
|
||||
// - Android has a hamburger drawer, iOS has bottom tabs
|
||||
// - The iOS nav bar is scrollable, Android is not
|
||||
// - Pull-to-refresh works differently, and Android has a button to trigger it too
|
||||
//
|
||||
// And these are all design time choices that doesn't have a single 'right'
|
||||
// answer.
|
||||
// ===========================================================================
|
||||
Widget _buildAndroid(context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(SongsTab.title),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.refresh),
|
||||
onPressed: () async => await _androidRefreshKey.currentState.show(),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.shuffle),
|
||||
onPressed: _togglePlatform,
|
||||
),
|
||||
],
|
||||
),
|
||||
drawer: widget.androidDrawer,
|
||||
body: RefreshIndicator(
|
||||
key: _androidRefreshKey,
|
||||
onRefresh: _refreshData,
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
itemBuilder: _listBuilder,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIos(context) {
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
CupertinoSliverNavigationBar(
|
||||
trailing: CupertinoButton(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Icon(CupertinoIcons.shuffle),
|
||||
onPressed: _togglePlatform,
|
||||
),
|
||||
),
|
||||
CupertinoSliverRefreshControl(
|
||||
onRefresh: _refreshData,
|
||||
),
|
||||
SliverSafeArea(
|
||||
top: false,
|
||||
sliver: SliverPadding(
|
||||
padding: EdgeInsets.symmetric(vertical: 12),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(_listBuilder),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(context) {
|
||||
return PlatformWidget(
|
||||
androidBuilder: _buildAndroid,
|
||||
iosBuilder: _buildIos,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user