Use Material Design on iOS

parent 28bbecce
......@@ -17,7 +17,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/ui/initial/initial_page.dart';
import 'package:pattle/src/ui/main/chat/chat_page.dart';
......@@ -34,43 +33,43 @@ import 'package:pattle/src/ui/main/overview/create/group/create_group_members_pa
import 'ui/main/overview/create/group/create_group_details_page.dart';
final routes = {
Routes.root: (Object params) => platformPageRoute(
Routes.root: (Object params) => MaterialPageRoute(
settings: RouteSettings(name: Routes.root),
builder: (context) => InitialPage(),
),
Routes.chats: (Object arguments) => platformPageRoute(
Routes.chats: (Object arguments) => MaterialPageRoute(
settings: RouteSettings(name: Routes.chats),
builder: (context) =>
arguments is Room ? ChatPage(arguments) : ChatOverviewPage(),
),
Routes.chatsSettings: (Object arguments) => platformPageRoute(
Routes.chatsSettings: (Object arguments) => MaterialPageRoute(
settings: RouteSettings(name: Routes.chatsSettings),
builder: (context) => ChatSettingsPage(arguments),
),
Routes.chatsNew: (Object arguments) => platformPageRoute(
Routes.chatsNew: (Object arguments) => MaterialPageRoute(
settings: RouteSettings(name: Routes.chatsNew),
builder: (context) => CreateGroupMembersPage(),
),
Routes.chatsNewDetails: (Object arguments) => platformPageRoute(
Routes.chatsNewDetails: (Object arguments) => MaterialPageRoute(
settings: RouteSettings(name: Routes.chatsNewDetails),
builder: (context) => CreateGroupDetailsPage(),
),
Routes.image: (Object arguments) => platformPageRoute(
Routes.image: (Object arguments) => MaterialPageRoute(
settings: RouteSettings(name: Routes.image),
builder: (context) => ImagePage(arguments)),
Routes.start: (Object params) => platformPageRoute(
Routes.start: (Object params) => MaterialPageRoute(
settings: RouteSettings(name: Routes.start),
builder: (context) => StartPage(),
),
Routes.startAdvanced: (Object params) => platformPageRoute(
Routes.startAdvanced: (Object params) => MaterialPageRoute(
settings: RouteSettings(name: Routes.startAdvanced),
builder: (context) => AdvancedPage(),
),
Routes.startUsername: (Object params) => platformPageRoute(
Routes.startUsername: (Object params) => MaterialPageRoute(
settings: RouteSettings(name: Routes.startUsername),
builder: (context) => UsernamePage(),
),
Routes.startPassword: (Object params) => platformPageRoute(
Routes.startPassword: (Object params) => MaterialPageRoute(
settings: RouteSettings(name: Routes.startPassword),
builder: (context) => PasswordPage(),
),
......@@ -97,7 +96,7 @@ class App extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return PlatformApp(
return MaterialApp(
onGenerateTitle: (BuildContext context) => l(context).appName,
localizationsDelegates: [
const AppLocalizationsDelegate(),
......@@ -111,12 +110,7 @@ class App extends StatelessWidget {
onGenerateRoute: (settings) {
return routes[settings.name](settings.arguments);
},
android: (_) => MaterialAppData(
theme: lightTheme,
),
ios: (_) => CupertinoAppData(
theme: MaterialBasedCupertinoThemeData(materialTheme: lightTheme),
),
theme: lightTheme,
);
}
}
......@@ -16,7 +16,6 @@
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/app.dart';
import 'package:pattle/src/ui/main/chat/chat_bloc.dart';
......@@ -82,21 +81,16 @@ class ChatPageState extends State<ChatPage> {
Widget avatar = Container();
final avatarUrl = avatarUrlOf(room);
if (avatarUrl != null) {
final circleAvatar = CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: MatrixImage(
avatarUrl,
width: 64,
height: 64,
),
);
avatar = PlatformWidget(
android: (_) => Hero(
tag: room.id,
child: circleAvatar,
avatar = Hero(
tag: room.id,
child: CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: MatrixImage(
avatarUrl,
width: 64,
height: 64,
),
),
ios: (_) => circleAvatar,
);
}
......@@ -109,7 +103,7 @@ class ChatPageState extends State<ChatPage> {
};
// TODO: typingUsers should not contain nulls
final title =
Widget title =
room.isSomeoneElseTyping && !room.typingUsers.any((u) => u == null)
? TitleWithSub(
title: ChatName(room: room),
......@@ -121,50 +115,19 @@ class ChatPageState extends State<ChatPage> {
)
: ChatName(room: room);
return PlatformScaffold(
return Scaffold(
backgroundColor: LightColors.red[50],
appBar: PlatformAppBar(
automaticallyImplyLeading: false,
android: (context) => MaterialAppBarData(
titleSpacing: 0,
title: settingsGestureDetector(
child: Row(
children: [
IconButton(
icon: Icon(Icons.arrow_back),
padding: EdgeInsets.all(0),
onPressed: () {
Navigator.pop(context);
},
),
avatar,
SizedBox(
width: 16,
),
Flexible(
child: title,
),
],
),
),
),
ios: (context) => CupertinoNavigationBarData(
automaticallyImplyLeading: true,
backgroundColor: CupertinoTheme.of(context).primaryColor,
actionsForegroundColor: Colors.white,
padding: EdgeInsetsDirectional.only(start: 0),
trailing: Padding(
padding: EdgeInsets.all(4),
child: AspectRatio(
aspectRatio: 1 / 1,
child: avatar,
),
),
title: settingsGestureDetector(
child: ChatName(
room: room,
style: TextStyle(color: Colors.white),
),
appBar: AppBar(
titleSpacing: 0,
title: settingsGestureDetector(
child: Row(
children: <Widget>[
avatar,
SizedBox(width: 16),
Expanded(
child: title,
),
],
),
),
),
......@@ -192,14 +155,6 @@ class ChatPageState extends State<ChatPage> {
Widget _buildInput() {
const elevation = 8.0;
final sendButton = PlatformIconButton(
androidIcon: Icon(Icons.send),
iosIcon: Icon(CupertinoIcons.forward),
onPressed: () {
bloc.sendMessage(textController.value.text);
textController.clear();
setState(() {});
});
if (bloc.room is JoinedRoom) {
return Material(
......@@ -207,51 +162,33 @@ class ChatPageState extends State<ChatPage> {
color: LightColors.red[50],
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: PlatformWidget(
android: (_) => Material(
elevation: elevation,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8), topRight: Radius.circular(8)),
color: Colors.white,
child: TextField(
controller: textController,
textInputAction: TextInputAction.newline,
autocorrect: true,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(8),
),
child: Material(
elevation: elevation,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8), topRight: Radius.circular(8)),
color: Colors.white,
child: TextField(
controller: textController,
textInputAction: TextInputAction.newline,
autocorrect: true,
textCapitalization: TextCapitalization.sentences,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(8),
),
filled: true,
hintText: l(context).typeAMessage,
suffixIcon: sendButton,
),
),
),
ios: (_) => Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: CupertinoTextField(
autocorrect: true,
textCapitalization: TextCapitalization.sentences,
controller: textController,
placeholder: l(context).typeAMessage,
suffix: sendButton,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
border: Border.all(
color: LightColors.red[100],
style: BorderStyle.solid,
width: 0.0,
),
color: Colors.white,
),
),
filled: true,
hintText: l(context).typeAMessage,
suffixIcon: IconButton(
icon: Icon(Icons.send),
onPressed: () {
bloc.sendMessage(textController.value.text);
textController.clear();
setState(() {});
},
),
],
),
),
),
),
......
......@@ -17,7 +17,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/ui/main/chat/settings/chat_settings_bloc.dart';
import 'package:pattle/src/ui/main/widgets/chat_name.dart';
......@@ -52,7 +51,7 @@ class ChatSettingsPageState extends State<ChatSettingsPage> {
@override
Widget build(BuildContext context) {
return PlatformScaffold(
return Scaffold(
backgroundColor: LightColors.red[50],
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
......
......@@ -19,7 +19,6 @@ import 'package:flutter/material.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/app.dart';
import 'package:pattle/src/ui/main/models/chat_item.dart';
import 'package:pattle/src/ui/main/widgets/platform_ink_well.dart';
import 'package:pattle/src/ui/util/matrix_image.dart';
import 'bubble.dart';
......@@ -153,7 +152,7 @@ class ImageBubbleState extends MessageBubbleState<ImageBubble> {
Positioned.fill(
child: Material(
color: Colors.transparent,
child: PlatformInkWell(
child: InkWell(
customBorder: border(),
onTap: () {
_onTap(context);
......
......@@ -19,7 +19,6 @@ import 'package:flutter/material.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/ui/main/models/chat_item.dart';
import 'package:pattle/src/di.dart' as di;
import 'package:pattle/src/ui/main/widgets/platform_ink_well.dart';
import 'package:pattle/src/ui/main/widgets/redacted.dart';
import 'bubble.dart';
......@@ -58,7 +57,7 @@ class RedactedBubbleState extends MessageBubbleState<RedactedBubble> {
@override
Widget buildMine(BuildContext context) {
return PlatformInkWell(
return InkWell(
onTap: () {},
customBorder: border(),
child: Padding(
......@@ -77,7 +76,7 @@ class RedactedBubbleState extends MessageBubbleState<RedactedBubble> {
@override
Widget buildTheirs(BuildContext context) {
return PlatformInkWell(
return InkWell(
onTap: () {},
customBorder: border(),
child: Padding(
......
......@@ -18,7 +18,6 @@
import 'package:flutter/material.dart';
import 'package:pattle/src/ui/main/models/chat_item.dart';
import 'package:pattle/src/ui/main/widgets/platform_ink_well.dart';
import 'package:pattle/src/ui/resources/theme.dart';
import 'package:pattle/src/ui/util/date_format.dart';
......@@ -88,7 +87,7 @@ abstract class StateBubbleState<T extends StateBubble> extends ItemState<T> {
elevation: 1,
color: LightColors.red[100],
borderRadius: StateBubble.borderRadius,
child: PlatformInkWell(
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: StateBubble.borderRadius,
),
......
......@@ -18,7 +18,6 @@
import 'package:flutter/material.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/ui/main/models/chat_item.dart';
import 'package:pattle/src/ui/main/widgets/platform_ink_well.dart';
import 'package:pattle/src/ui/util/future_or_builder.dart';
import 'package:pattle/src/ui/util/user.dart';
import 'package:url_launcher/url_launcher.dart';
......@@ -147,7 +146,7 @@ class TextBubbleState extends MessageBubbleState<TextBubble> {
Widget buildMine(BuildContext context) {
final needsBorder = widget.isRepliedTo && widget.reply.sender == widget.me;
return PlatformInkWell(
return InkWell(
onTap: () {},
customBorder: border(),
child: CustomPaint(
......@@ -179,7 +178,7 @@ class TextBubbleState extends MessageBubbleState<TextBubble> {
? buildSender(context)
: Container(width: 0);
return PlatformInkWell(
return InkWell(
onTap: () {},
customBorder: border(),
child: CustomPaint(
......
......@@ -16,7 +16,6 @@
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:pattle/src/app.dart';
import 'package:pattle/src/ui/main/overview/models/chat_overview.dart';
import 'package:pattle/src/ui/main/overview/chat_overview_bloc.dart';
......@@ -51,23 +50,9 @@ class ChatOverviewPageState extends State<ChatOverviewPage> {
@override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: PlatformAppBar(
return Scaffold(
appBar: AppBar(
title: Text(l(context).appName),
ios: (_) => CupertinoNavigationBarData(
backgroundColor: CupertinoTheme.of(context).primaryColor,
title: Text(
l(context).appName,
style: TextStyle(color: Colors.white),
),
trailing: PlatformIconButton(
icon: Icon(
CupertinoIcons.add_circled,
color: CupertinoColors.white,
),
onPressed: goToCreateGroup,
),
),
),
body: Column(
children: <Widget>[
......@@ -77,11 +62,9 @@ class ChatOverviewPageState extends State<ChatOverviewPage> {
)
],
),
android: (_) => MaterialScaffoldData(
floatingActionButton: FloatingActionButton(
onPressed: goToCreateGroup,
child: Icon(Icons.chat),
),
floatingActionButton: FloatingActionButton(
onPressed: goToCreateGroup,
child: Icon(Icons.chat),
),
);
}
......@@ -98,14 +81,14 @@ class ChatOverviewPageState extends State<ChatOverviewPage> {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
widget = Center(child: PlatformCircularProgressIndicator());
widget = Center(child: CircularProgressIndicator());
break;
case ConnectionState.active:
case ConnectionState.done:
final chats = snapshot.data;
if (chats == null || chats.isEmpty) {
return Center(child: PlatformCircularProgressIndicator());
return Center(child: CircularProgressIndicator());
}
widget = ListView.separated(
......
......@@ -16,7 +16,6 @@
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:pattle/src/app.dart';
import 'package:pattle/src/ui/main/widgets/error.dart';
import 'package:pattle/src/ui/main/widgets/user_avatar.dart';
......@@ -52,22 +51,9 @@ class CreateGroupDetailsPageState extends State<CreateGroupDetailsPage> {
@override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: PlatformAppBar(
return Scaffold(
appBar: AppBar(
title: Text(l(context).newGroup),
ios: (_) => CupertinoNavigationBarData(
backgroundColor: CupertinoTheme.of(context).primaryColor,
actionsForegroundColor: Colors.white,
title: Text(
l(context).appName,
style: TextStyle(color: Colors.white),
),
trailing: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: createGroup,
child: Text(l(context).confirm),
),
),
),
body: Column(
children: <Widget>[
......@@ -78,17 +64,15 @@ class CreateGroupDetailsPageState extends State<CreateGroupDetailsPage> {
Expanded(
child: Padding(
padding: EdgeInsets.all(16),
child: PlatformTextField(
child: TextField(
onChanged: (text) {
bloc.groupName = text;
},
maxLines: 1,
android: (_) => MaterialTextFieldData(
decoration: InputDecoration(
labelText: l(context).groupName, filled: true),
decoration: InputDecoration(
labelText: l(context).groupName,
filled: true,
),
ios: (_) => CupertinoTextFieldData(
placeholder: l(context).groupName),
),
),
)
......@@ -98,28 +82,24 @@ class CreateGroupDetailsPageState extends State<CreateGroupDetailsPage> {
Expanded(child: _buildUserList(context))
],
),
android: (_) => MaterialScaffoldData(
floatingActionButton: FloatingActionButton(
onPressed: createGroup,
child: StreamBuilder<bool>(
stream: bloc.isCreatingRoom,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
final isCreatingRoom = snapshot.data ?? false;
if (isCreatingRoom) {
return SizedBox(
width: 24,
height: 24,
child: PlatformCircularProgressIndicator(
android: (_) => MaterialProgressIndicatorData(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
);
} else {
return Icon(Icons.check);
}
},
),
floatingActionButton: FloatingActionButton(
onPressed: createGroup,
child: StreamBuilder<bool>(
stream: bloc.isCreatingRoom,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
final isCreatingRoom = snapshot.data ?? false;
if (isCreatingRoom) {
return SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
);
} else {
return Icon(Icons.check);
}
},
),
),
);
......
......@@ -16,7 +16,6 @@
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:matrix_sdk/matrix_sdk.dart';
import 'package:pattle/src/app.dart';
import 'package:pattle/src/ui/main/widgets/error.dart';
......@@ -44,22 +43,9 @@ class CreateGroupMembersPageState extends State<CreateGroupMembersPage> {
@override
Widget build(BuildContext context) {
return PlatformScaffold(
appBar: PlatformAppBar(
return Scaffold(
appBar: AppBar(
title: Text(l(context).newGroup),
ios: (_) => CupertinoNavigationBarData(
backgroundColor: CupertinoTheme.of(context).primaryColor,
actionsForegroundColor: Colors.white,
title: Text(
l(context).appName,
style: TextStyle(color: Colors.white),
),
trailing: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: goToNext,
child: Text(l(context).next),
),
),
),
body: Column(
children: <Widget>[
......@@ -67,11 +53,9 @@ class CreateGroupMembersPageState extends State<CreateGroupMembersPage> {
Expanded(child: _buildUserList(context))
],
),
android: (_) => MaterialScaffoldData(
floatingActionButton: FloatingActionButton(
onPressed: goToNext,
child: Icon(Icons.arrow_forward),
),
floatingActionButton: FloatingActionButton(
onPressed: goToNext,
child: Icon(Icons.arrow_forward),
),
);
}
......
// Copyright (C) 2019 Wilko Manger
//
// This file is part of Pattle.
//
// Pattle is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pattle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
class PlatformInkWell extends PlatformWidgetBase<Widget, InkWell> {
final Widget child;
final GestureTapCallback onTap;
final ShapeBorder customBorder;
PlatformInkWell({this.child, this.onTap, this.customBorder});
@override
InkWell createAndroidWidget(BuildContext context) {
return InkWell(
customBorder: customBorder,
onTap: onTap,
child: child,
);
}
@override
Widget createIosWidget(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: child,
);
}
}
......@@ -16,7 +16,6 @@
// along with Pattle. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:pattle/src/ui/resources/localizations.dart';
import 'package:pattle/src/ui/start/start_bloc.dart';
......@@ -36,25 +35,19 @@ class AdvancedPageState extends State<AdvancedPage> {
@override
Widget build(BuildContext context) {
return PlatformScaffold(
iosContentPadding: true,
appBar: PlatformAppBar(
title: PlatformWidget(
android: (_) => Text(
l(context).advanced,
style: TextStyle(
color: Theme.of(context).primaryColor,
),
),
ios: (_) => Text(l(context).advanced),
),
android: (_) => MaterialAppBarData(
iconTheme: IconThemeData(
return Scaffold(
appBar: AppBar(
title: Text(
l(context).advanced,
style: TextStyle(
color: Theme.of(context).primaryColor,
),
elevation: 0,
backgroundColor: const Color(0x00000000),
),
iconTheme: IconThemeData(
color: Theme.of(context).primaryColor,
),
elevation: 0,
backgroundColor: const Color(0x00000000),
),
body: Container(
margin: EdgeInsets.all(16),
......@@ -64,11 +57,10 @@ class AdvancedPageState extends State<AdvancedPage> {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
PlatformWidget(
android: (_) => Icon(Icons.home,
color: Theme.of(context).hintColor, size: 32),
ios: (_) => Icon(CupertinoIcons.home,
color: Theme.of(context).hintColor, size: 32),
Icon(
Icons.home,
color: Theme.of(context).hintColor,
size: 32,
),
SizedBox(width: 16),
Flexible(
......@@ -84,15 +76,13 @@ class AdvancedPageState extends State<AdvancedPage> {
errorText = null;
}
return PlatformTextField(
return TextField(
controller: homeserverTextController,
android: (_) => MaterialTextFieldData(
decoration: InputDecoration(
filled: true,
labelText: l(context).homeserver,
hintText: bloc.userIdDomain.toString(),
errorText: errorText,
),
decoration: InputDecoration(