ui: Make the UI much more consistent

This commit is contained in:
PapaTutuWawa 2021-12-25 19:01:06 +01:00
parent be5a32dcf0
commit 35da08608b
15 changed files with 170 additions and 225 deletions

View File

@ -21,6 +21,10 @@ import "redux/conversation/actions.dart";
import "redux/state.dart";
import 'package:get_it/get_it.dart';
// TODO: Replace all single quotes with double quotes
// TODO: Replace all Column(children: [ Padding(), Padding, ...]) with a
// Padding(padding: ..., child: Column(children: [ ... ]))
void main() {
GetIt.I.registerSingleton<RosterRepository>(RosterRepository());

View File

@ -7,3 +7,9 @@ const Color BUBBLE_COLOR_SENT = Color.fromRGBO(162, 68, 173, 1.0);
const Color BUBBLE_COLOR_RECEIVED = Color.fromRGBO(44, 62, 80, 1.0);
const double PADDING_VERY_LARGE = 64.0;
const double FONTSIZE_TITLE = 40;
const double FONTSIZE_SUBTITLE = 25;
const double FONTSIZE_APPBAR = 20;
const double FONTSIZE_BODY = 15;
const double FONTSIZE_SUBBODY = 10;

View File

@ -41,20 +41,7 @@ class AddContactPage extends StatelessWidget {TextEditingController controller =
addContact: (jid) => store.dispatch(AddContactAction(jid: jid))
),
builder: (context, viewModel) => Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton(),
Text(
"Add new contact",
style: TextStyle(
fontSize: 19
)
)
]
)
),
appBar: BorderlessTopbar.simple(title: "Add new contact"),
// TODO: The TextFields look a bit too smal
// TODO: Hide the LinearProgressIndicator if we're not doing anything
// TODO: Disable the inputs and the BackButton if we're working on loggin in

View File

@ -123,52 +123,16 @@ class ConversationPage extends StatelessWidget {
),
builder: (context, viewModel) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
boxShadow: true,
children: [
Center(
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.arrow_back)
)
),
Center(
child: InkWell(
child: Row(
children: [
Padding(
padding: EdgeInsets.only(left: 16.0),
child: AvatarWrapper(
appBar: BorderlessTopbar.avatarAndName(
avatar: AvatarWrapper(
radius: 25.0,
avatarUrl: viewModel.conversation.avatarUrl,
altText: viewModel.conversation.title[0]
)
),
Padding(
padding: EdgeInsets.only(left: 2.0),
child: Text(
viewModel.conversation.title,
style: TextStyle(
fontSize: 20
)
)
)
]
),
onTap: () {
Navigator.pushNamed(context, "/conversation/profile", arguments: ProfilePageArguments(conversation: viewModel.conversation, isSelfProfile: false));
}
)
),
Spacer(),
// TODO
// TODO: Make the icon depend on the current state
// TODO: Gray-out if the contact does not support anything but plaintext
// TODO: Use enum
title: viewModel.conversation.title,
onTapFunction: () {},
showBackButton: true,
extra: [
PopupMenuButton(
onSelected: (result) {
if (result == "omemo") {
@ -198,7 +162,6 @@ class ConversationPage extends StatelessWidget {
]
)
]
)
),
body: Column(
children: [

View File

@ -27,33 +27,17 @@ class ConversationsPage extends StatelessWidget {
@override
Widget build(BuildContext buildContext) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
InkWell(
onTap: () => Navigator.pushNamed(buildContext, "/conversation/profile", arguments: ProfilePageArguments(isSelfProfile: true)),
child: Row(
children: [
Padding(
padding: EdgeInsets.only(right: 3.0),
child: AvatarWrapper(
appBar: BorderlessTopbar.avatarAndName(
avatar: AvatarWrapper(
radius: 20.0,
avatarUrl: "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse3.mm.bing.net%2Fth%3Fid%3DOIP.MkXhyVPrn9eQGC1CTOyTYAHaHa%26pid%3DApi&f=1",
// TODO
altText: "?"
)
),
Text(
"Ojou",
style: TextStyle(
fontSize: 18
)
)
]
)
),
Spacer(),
title: "Oujou",
onTapFunction: () => Navigator.pushNamed(buildContext, "/conversation/profile", arguments: ProfilePageArguments(isSelfProfile: true)),
showBackButton: false,
extra: [
PopupMenuButton(
onSelected: (ConversationsOptions result) {
if (result == ConversationsOptions.SETTINGS) {
@ -70,7 +54,6 @@ class ConversationsPage extends StatelessWidget {
]
)
]
)
),
body: StoreConnector<MoxxyState, _ConversationsListViewModel>(
converter: (store) => _ConversationsListViewModel(

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import "package:moxxyv2/ui/constants.dart";
class IntroPage extends StatelessWidget {
@override
@ -14,7 +15,7 @@ class IntroPage extends StatelessWidget {
child: Text(
"moxxy",
style: TextStyle(
fontSize: 40
fontSize: FONTSIZE_TITLE
)
)
),
@ -26,11 +27,11 @@ class IntroPage extends StatelessWidget {
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE),
child: Text(
"An experiment into building a modern, easy and beautiful XMPP client.",
style: TextStyle(
fontSize: 15
fontSize: FONTSIZE_BODY
)
)
),
@ -38,7 +39,7 @@ class IntroPage extends StatelessWidget {
children: [
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE),
child: ElevatedButton(
child: Text("Login"),
onPressed: () => Navigator.pushNamed(context, "/login")
@ -49,11 +50,11 @@ class IntroPage extends StatelessWidget {
),
Spacer(),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE),
child: Text(
"Have no XMPP account? No worries, creating one is really easy.",
style: TextStyle(
fontSize: 15
fontSize: FONTSIZE_BODY
)
)
),
@ -61,7 +62,7 @@ class IntroPage extends StatelessWidget {
children: [
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0).add(EdgeInsets.only(bottom: 64.0)),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE).add(EdgeInsets.only(bottom: PADDING_VERY_LARGE)),
child: TextButton(
child: Text("Register"),
onPressed: () => Navigator.pushNamed(context, "/register")

View File

@ -42,20 +42,7 @@ class LoginPage extends StatelessWidget {
showPassword: store.state.loginPageState.showPassword
),
builder: (context, viewModel) => Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton(),
Text(
"Login",
style: TextStyle(
fontSize: 19
)
)
]
)
),
appBar: BorderlessTopbar.simple(title: "Login"),
// TODO: The TextFields look a bit too smal
// TODO: Hide the LinearProgressIndicator if we're not doing anything
// TODO: Disable the inputs and the BackButton if we're working on loggin in

View File

@ -62,20 +62,7 @@ class NewConversationPage extends StatelessWidget {
Widget build(BuildContext context) {
var roster = GetIt.I.get<RosterRepository>().getAllRosterItems();
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton(),
Text(
"Start new chat",
style: TextStyle(
fontSize: 17
)
)
]
)
),
appBar: BorderlessTopbar.simple(title: "Start new chat"),
body: StoreConnector<MoxxyState, _NewConversationViewModel>(
converter: (store) => _NewConversationViewModel(
addConversation: (c) => store.dispatch(

View File

@ -15,7 +15,7 @@ class PostRegistrationPage extends StatelessWidget {
child: Text(
"This is you!",
style: TextStyle(
fontSize: 40
fontSize: FONTSIZE_TITLE
)
)
),
@ -45,7 +45,7 @@ class PostRegistrationPage extends StatelessWidget {
"Testuser",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25
fontSize: FONTSIZE_SUBTITLE
)
),
Text("testuser@someprovider.net")
@ -57,16 +57,16 @@ class PostRegistrationPage extends StatelessWidget {
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0).add(EdgeInsets.only(top: 16.0)),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE).add(EdgeInsets.only(top: 16.0)),
child: Text(
"We have auto-generated a password for you. You should write it down somewhere safe.",
style: TextStyle(
fontSize: 15
fontSize: FONTSIZE_BODY
)
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0).add(EdgeInsets.only(top: 16.0)),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE).add(EdgeInsets.only(top: 16.0)),
child: ExpansionTile(
title: Text("Show password"),
children: [
@ -75,7 +75,7 @@ class PostRegistrationPage extends StatelessWidget {
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE),
child: ExpansionTile(
title: Text("Advanced settings"),
children: [
@ -95,11 +95,16 @@ class PostRegistrationPage extends StatelessWidget {
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0).add(EdgeInsets.only(top: 16.0)),
child: Text("You can now be contacted by your XMPP address. If you want to set a display name, just tap the text next to the profile picture.")
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE).add(EdgeInsets.only(top: 16.0)),
child: Text(
"You can now be contacted by your XMPP address. If you want to set a display name, just tap the text next to the profile picture.",
style: TextStyle(
fontSize: FONTSIZE_BODY
)
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 64.0),
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE),
child: Row(
children: [
Expanded(

View File

@ -59,20 +59,7 @@ class RegistrationPage extends StatelessWidget {
performRegistration: () => store.dispatch(PerformRegistrationAction())
),
builder: (context, viewModel) => Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton(),
Text(
"Register",
style: TextStyle(
fontSize: 19
)
)
]
)
),
appBar: BorderlessTopbar.simple(title: "Register"),
// TODO: The TextFields look a bit too smal
// TODO: Hide the LinearProgressIndicator if we're not doing anything
// TODO: Disable the inputs and the BackButton if we're working on loggin in
@ -84,7 +71,12 @@ class RegistrationPage extends StatelessWidget {
),
Padding(
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE, vertical: 16.0),
child: Text("XMPP is a lot like e-mail: You can send e-mails to people who are not using your specific e-mail provider. As such, there are a lot of XMPP providers. To help you, we chose a random one from a curated list. You only have to pick a username.")
child: Text(
"XMPP is a lot like e-mail: You can send e-mails to people who are not using your specific e-mail provider. As such, there are a lot of XMPP providers. To help you, we chose a random one from a curated list. You only have to pick a username.",
style: TextStyle(
fontSize: FONTSIZE_BODY
)
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE).add(EdgeInsets.only(bottom: 8.0)),

View File

@ -17,14 +17,7 @@ class SettingsAboutPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton()
]
)
),
appBar: BorderlessTopbar.simple(title: "About"),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: PADDING_VERY_LARGE),
child: Column(

View File

@ -81,14 +81,7 @@ class SettingsLicensesPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton()
]
)
),
appBar: BorderlessTopbar.simple(title: "Licenses"),
body: ListView.builder(
itemCount: _USED_LIBRARIES.length,
itemBuilder: (context, index) => LicenseRow(library: _USED_LIBRARIES[index])

View File

@ -8,20 +8,7 @@ class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(60),
child: BorderlessTopbar(
children: [
BackButton(),
Text(
"Settings",
style: TextStyle(
fontSize: 20
)
)
]
)
),
appBar: BorderlessTopbar.simple(title: "Settings"),
body: SettingsList(
// TODO: Seems hacky
darkBackgroundColor: Color(0xff303030),

View File

@ -56,7 +56,7 @@ class ChatBubble extends StatelessWidget {
this.messageContent,
style: TextStyle(
color: Colors.white,
fontSize: 17
fontSize: FONTSIZE_BODY
)
),
Row(
@ -67,7 +67,7 @@ class ChatBubble extends StatelessWidget {
child: Text(
timestampString,
style: TextStyle(
fontSize: 10,
fontSize: FONTSIZE_SUBBODY,
color: Colors.grey
)
)

View File

@ -1,14 +1,71 @@
import "dart:collection";
import 'package:flutter/material.dart';
import "package:moxxyv2/ui/widgets/avatar.dart";
/*
Provides a Signal-like topbar without borders or anything else
*/
class BorderlessTopbar extends StatelessWidget {
class BorderlessTopbar extends StatelessWidget implements PreferredSizeWidget {
List<Widget> children;
// TODO: Implement
bool boxShadow;
BorderlessTopbar({ required this.children, this.boxShadow = false });
/*
* A simple borderless topbar that displays just the back button (if wanted) and a
* Text() title
*/
BorderlessTopbar.simple({ required String title , List<Widget>? extra, bool showBackButton = true }) : boxShadow = false, children = [
Visibility(
child: BackButton(),
visible: showBackButton
),
Text(
title,
style: TextStyle(
fontSize: 20
)
),
...(extra ?? [])
];
/*
* Displays a clickable avatar and title and a back button, if wanted
*/
// TODO: Reuse BorderlessTopbar.simple
BorderlessTopbar.avatarAndName({ required AvatarWrapper avatar, required String title, void Function()? onTapFunction, List<Widget>? extra, bool showBackButton = true }) : boxShadow = false, children = [
Visibility(
child: BackButton(),
visible: showBackButton
),
Center(
child: InkWell(
child: Row(
children: [
avatar,
Padding(
padding: EdgeInsets.only(left: 8.0),
child: Text(
title,
style: TextStyle(
fontSize: 20
)
)
)
]
),
onTap: onTapFunction
)
),
Spacer(),
...(extra ?? [])
];
@override
final Size preferredSize = Size.fromHeight(60);
@override
Widget build(BuildContext context) {
return SafeArea(