chore: style tweaks

This commit is contained in:
Rongjian Zhang 2019-10-02 14:58:11 +08:00
parent b119a425cd
commit 378f8d10d8
25 changed files with 270 additions and 208 deletions

View File

@ -59,7 +59,7 @@ class _HomeState extends State<Home> {
);
}
List<BottomNavigationBarItem> _buildNavigationItems() {
List<BottomNavigationBarItem> get _navigationItems {
return [
BottomNavigationBarItem(
icon: Icon(Icons.rss_feed),
@ -139,7 +139,10 @@ class _HomeState extends State<Home> {
primaryColor: PrimerColors.blue500,
),
child: CupertinoTabScaffold(
tabBar: CupertinoTabBar(items: _buildNavigationItems()),
tabBar: CupertinoTabBar(
items: _navigationItems,
// backgroundColor: PrimerColors.gray000, // TODO:
),
tabBuilder: (context, index) {
return CupertinoTabView(builder: (context) {
return _buildScreen(index);
@ -156,7 +159,7 @@ class _HomeState extends State<Home> {
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
selectedItemColor: PrimerColors.blue500,
items: _buildNavigationItems(),
items: _navigationItems,
currentIndex: active,
type: BottomNavigationBarType.fixed,
onTap: (int index) {

View File

@ -36,6 +36,12 @@ class PickerGroupItem<T> {
});
}
class SelectorItem<T> {
T value;
String text;
SelectorItem({@required this.value, @required this.text});
}
// No animation. For replacing route
// TODO: Go back
class StaticRoute extends PageRouteBuilder {
@ -224,8 +230,27 @@ class ThemeModel with ChangeNotifier {
}
}
static Timer _debounce;
showSelector<T>({
@required BuildContext context,
@required Iterable<SelectorItem<T>> items,
@required T selected,
}) async {
switch (theme) {
case AppThemeType.cupertino:
default:
return showMenu<T>(
context: context,
initialValue: selected,
items: items
.map((item) =>
PopupMenuItem(value: item.value, child: Text(item.text)))
.toList(),
position: RelativeRect.fromLTRB(1, 10, 0, 0),
);
}
}
static Timer _debounce;
String _selectedItem;
showPicker(BuildContext context, PickerGroupItem<String> groupItem) async {

50
lib/scaffolds/common.dart Normal file
View File

@ -0,0 +1,50 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
class CommonScaffold extends StatelessWidget {
final Widget title;
final Widget body;
final Widget action;
final PreferredSizeWidget bottom;
final Color backgroundColor;
CommonScaffold({
@required this.title,
@required this.body,
this.action,
this.bottom,
this.backgroundColor = Colors.white,
});
@override
Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return CupertinoPageScaffold(
backgroundColor: backgroundColor,
navigationBar: CupertinoNavigationBar(
middle: title,
trailing: action,
backgroundColor: PrimerColors.gray000,
// border: Border(),
),
child: SafeArea(child: body),
);
default:
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
title: title,
actions: [
if (action != null) action,
],
bottom: bottom,
),
body: body,
);
}
}
}

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:provider/provider.dart';
import '../widgets/error_reload.dart';
@ -24,16 +24,14 @@ class ListStatefulScaffold<T, K> extends StatefulWidget {
final Widget Function(T payload) itemBuilder;
final Future<ListPayload<T, K>> Function() onRefresh;
final Future<ListPayload<T, K>> Function(K cursor) onLoadMore;
final Widget borderView;
ListStatefulScaffold({
@required this.title,
@required double leftPadding,
@required this.itemBuilder,
@required this.onRefresh,
@required this.onLoadMore,
this.trailingBuiler,
}) : borderView = BorderView(leftPadding: leftPadding);
});
@override
_ListStatefulScaffoldState<T, K> createState() =>
@ -144,7 +142,7 @@ class _ListStatefulScaffoldState<T, K>
}
if (index % 2 == 1) {
return widget.borderView;
return borderView;
}
return widget.itemBuilder(items[index ~/ 2]);
@ -212,7 +210,7 @@ class _ListStatefulScaffoldState<T, K>
return CommonScaffold(
title: widget.title,
body: _buildBody(),
trailing: widget.trailingBuiler == null ? null : widget.trailingBuiler(),
action: widget.trailingBuiler == null ? null : widget.trailingBuiler(),
);
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:primer/primer.dart';
class RefreshStatefulScaffoldPayload<T> {
bool loading;
@ -73,6 +75,7 @@ class _RefreshStatefulScaffoldState<T>
@override
Widget build(BuildContext context) {
return CommonScaffold(
backgroundColor: _data == null ? Colors.white : PrimerColors.gray100,
title: widget.title,
body: RefreshWrapper(
onRefresh: _refresh,
@ -83,7 +86,7 @@ class _RefreshStatefulScaffoldState<T>
reload: _refresh,
),
),
trailing: _trailing,
action: _trailing,
);
}
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:git_touch/scaffolds/common.dart';
class SingleScaffold extends StatelessWidget {
final Widget title;
@ -17,7 +17,7 @@ class SingleScaffold extends StatelessWidget {
return CommonScaffold(
title: title,
body: Scrollbar(child: SingleChildScrollView(child: body)),
trailing: trailing,
action: trailing,
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:provider/provider.dart';
@ -49,7 +50,7 @@ class TabScaffold extends StatelessWidget {
final scaffold = CommonScaffold(
title: _buildTitle(context),
body: RefreshWrapper(body: body, onRefresh: onRefresh),
trailing: trailing,
action: trailing,
bottom: TabBar(
onTap: onTabSwitch,
tabs: tabs.map((text) => Tab(text: text.toUpperCase())).toList(),

View File

@ -5,43 +5,6 @@ import 'package:git_touch/widgets/error_reload.dart';
import 'package:git_touch/widgets/loading.dart';
import 'package:provider/provider.dart';
class CommonScaffold extends StatelessWidget {
final Widget title;
final Widget body;
final Widget trailing;
final PreferredSizeWidget bottom;
CommonScaffold({
@required this.title,
@required this.body,
this.trailing,
this.bottom,
});
@override
Widget build(BuildContext context) {
switch (Provider.of<ThemeModel>(context).theme) {
case AppThemeType.cupertino:
return CupertinoPageScaffold(
navigationBar:
CupertinoNavigationBar(middle: title, trailing: trailing),
child: SafeArea(child: body),
);
default:
return Scaffold(
appBar: AppBar(
title: title,
actions: [
if (trailing != null) trailing,
],
bottom: bottom,
),
body: body,
);
}
}
}
class RefreshWrapper extends StatelessWidget {
final Widget body;
final void Function() onRefresh;

View File

@ -84,7 +84,6 @@ class CommitsScreen extends StatelessWidget {
Widget build(BuildContext context) {
return ListStatefulScaffold(
title: AppBarTitle('Commits'),
leftPadding: 52,
onRefresh: () => _query(context),
onLoadMore: (cursor) => _query(context, cursor),
itemBuilder: (payload) {

View File

@ -44,7 +44,7 @@ class CreditsScreen extends StatelessWidget {
title: Text('Credits'),
body: Column(
children: <Widget>[
borderView1,
verticalGap,
TableView(
headerText: 'packages',
items: projects.map((t) {
@ -54,7 +54,7 @@ class CreditsScreen extends StatelessWidget {
);
}),
),
borderView1,
verticalGap,
TableView(
headerText: 'fonts',
items: fonts.map((font) {

View File

@ -49,7 +49,6 @@ class IssuesScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListStatefulScaffold(
leftPadding: 34,
title: AppBarTitle(
(isPullRequest ? 'Pull requests' : 'Issues') + ' of $owner/$name'),
onRefresh: () => _query(context),

View File

@ -69,7 +69,6 @@ class NewsScreenState extends State<NewsScreen> {
Widget build(context) {
return ListStatefulScaffold<EventPayload, int>(
title: AppBarTitle('News'),
leftPadding: 62,
itemBuilder: (payload) => EventItem(payload),
onRefresh: fetchEvents,
onLoadMore: (page) => fetchEvents(page),

View File

@ -39,7 +39,7 @@ class OrganizationScreen extends StatelessWidget {
if (items.isEmpty) return [];
return [
borderView1,
verticalGap,
if (title != null) TableViewHeader(title),
...join(
borderView,
@ -126,7 +126,7 @@ class OrganizationScreen extends StatelessWidget {
screenBuilder: (context) => UsersScreen.members(login),
),
]),
borderView1,
verticalGap,
TableView(
hasIcon: true,
items: [

View File

@ -73,7 +73,6 @@ class RepositoriesScreen extends StatelessWidget {
Widget build(BuildContext context) {
return ListStatefulScaffold(
title: AppBarTitle(title),
leftPadding: 42,
onRefresh: () => _queryRepos(context),
onLoadMore: (cursor) => _queryRepos(context, cursor),
itemBuilder: (payload) => RepositoryItem(payload),

View File

@ -242,9 +242,10 @@ class RepositoryScreen extends StatelessWidget {
),
],
),
borderView1,
verticalGap,
if ((data['languages']['edges'] as List).isNotEmpty)
Container(
color: Colors.white,
padding: const EdgeInsets.all(_languageBarPadding),
child: ClipRRect(
borderRadius: BorderRadius.circular(2),
@ -304,7 +305,7 @@ class RepositoryScreen extends StatelessWidget {
),
],
),
borderView1,
verticalGap,
TableView(
hasIcon: true,
items: [
@ -366,12 +367,14 @@ class RepositoryScreen extends StatelessWidget {
),
],
),
borderView1,
verticalGap,
if (readme != null)
Container(
padding: EdgeInsets.all(16),
padding: EdgeInsets.all(12),
color: Colors.white,
child: MarkdownView(readme),
),
verticalGap,
],
);
},

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/widgets/issue_item.dart';
import 'package:git_touch/widgets/loading.dart';
import 'package:git_touch/widgets/user_item.dart';

View File

@ -25,14 +25,14 @@ class SettingsScreen extends StatelessWidget {
title: AppBarTitle('Settings'),
body: Column(
children: <Widget>[
borderView1,
verticalGap,
TableView(headerText: 'ACCOUNTS', items: [
TableViewItem(
text: Text('Switch to another account'),
screenBuilder: (_) => LoginScreen(),
),
]),
borderView1,
verticalGap,
TableView(headerText: 'THEME', items: [
TableViewItem(
text: Text('Material'),
@ -57,7 +57,7 @@ class SettingsScreen extends StatelessWidget {
hideRightChevron: true,
),
]),
borderView1,
verticalGap,
TableView(headerText: 'ABOUT', items: [
TableViewItem(
text: Text('Source Code'),

View File

@ -86,16 +86,14 @@ class UserScreen extends StatelessWidget {
if (items.isEmpty) return [];
return [
borderView1,
verticalGap,
if (title != null) TableViewHeader(title),
borderView,
...join(
borderView,
items.map((item) {
return RepositoryItem(item);
}).toList(),
),
borderView,
];
}
@ -216,12 +214,9 @@ class UserScreen extends StatelessWidget {
screenBuilder: (context) => UsersScreen.following(login),
),
]),
borderView,
borderView1,
borderView,
verticalGap,
_buildContributions(contributions),
borderView,
borderView1,
verticalGap,
TableView(
hasIcon: true,
items: [
@ -266,7 +261,7 @@ class UserScreen extends StatelessWidget {
],
),
..._buildRepos(data),
borderView1,
verticalGap,
],
);
},

View File

@ -68,7 +68,6 @@ class UsersScreen extends StatelessWidget {
Widget build(BuildContext context) {
return ListStatefulScaffold(
title: AppBarTitle(title),
leftPadding: 68,
onRefresh: () => _queryUsers(context),
onLoadMore: (cursor) => _queryUsers(context, cursor),
itemBuilder: (payload) => UserItem.fromData(payload),

View File

@ -5,6 +5,7 @@ import 'package:flutter/gestures.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/screens/repository.dart';
import 'package:git_touch/screens/user.dart';
import 'package:git_touch/widgets/border_view.dart';
import 'package:intl/intl.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
@ -123,32 +124,8 @@ bool isNotNullOrEmpty(String text) {
return text != null && text.isNotEmpty;
}
class BorderView extends StatelessWidget {
final double height;
final Color color;
final double leftPadding;
const BorderView({
this.height = 0, // One physical pixel.
this.color = PrimerColors.gray400,
this.leftPadding = 0,
});
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(left: leftPadding),
decoration: BoxDecoration(
border: Border(
top: BorderSide(color: color, width: height),
),
),
);
}
}
const borderView = BorderView();
const borderView1 = BorderView(height: 20, color: PrimerColors.gray100);
const verticalGap = SizedBox(height: 20);
String getBranchQueryKey(String branch, {bool withParams = false}) {
if (branch == null) return 'defaultBranchRef';

View File

@ -32,11 +32,13 @@ class ActionButton extends StatelessWidget {
final String title;
final List<ActionItem> items;
final IconData iconData;
final int selected;
ActionButton({
@required this.title,
@required this.items,
this.iconData = Icons.more_vert,
this.selected,
});
@override
@ -53,7 +55,13 @@ class ActionButton extends StatelessWidget {
title: Text(title),
actions: items.asMap().entries.map((entry) {
return CupertinoActionSheetAction(
child: Text(entry.value.text),
child: Text(
entry.value.text,
style: TextStyle(
fontWeight: selected == entry.key
? FontWeight.w500
: FontWeight.w400),
),
onPressed: () {
Navigator.pop(context, entry.key);
},
@ -78,6 +86,7 @@ class ActionButton extends StatelessWidget {
default:
return PopupMenuButton(
icon: Icon(iconData),
initialValue: selected,
itemBuilder: (context) {
return items.asMap().entries.map((entry) {
return PopupMenuItem(
@ -86,8 +95,8 @@ class ActionButton extends StatelessWidget {
);
}).toList();
},
onSelected: (selected) {
items[selected].onPress();
onSelected: (value) {
items[value].onPress();
},
);
}

View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:primer/primer.dart';
class BorderView extends StatelessWidget {
final double height;
final Color color;
final double leftPadding;
const BorderView({
this.height = 1,
this.color = PrimerColors.gray100,
this.leftPadding = 0,
});
@override
Widget build(BuildContext context) {
final b = SizedBox(
height: height,
child: DecoratedBox(
decoration: BoxDecoration(color: color),
),
);
if (leftPadding == 0) {
return b;
}
return Row(
children: <Widget>[
SizedBox(
width: leftPadding,
height: height,
child: DecoratedBox(
decoration: BoxDecoration(color: PrimerColors.white),
),
),
Expanded(child: b),
],
);
// Physical pixel
// return Container(
// margin: EdgeInsets.only(left: leftPadding),
// decoration: BoxDecoration(
// border: Border(
// top: BorderSide(color: color, width: height),
// ),
// ),
// );
}
}

View File

@ -120,63 +120,58 @@ class RepositoryItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
var widget = Container(
padding: EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Avatar(url: payload['owner']['avatarUrl'], size: 12),
SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: join(SizedBox(height: 8), <Widget>[
Row(
children: <Widget>[
Text(
payload['owner']['login'] + ' / ',
style: TextStyle(
fontSize: inRepoScreen ? 18 : 16,
color: PrimerColors.blue500,
fontWeight: FontWeight.w500,
// TODO: text style
return Link(
screenBuilder: inRepoScreen
? null
: (_) => RepositoryScreen(payload['owner']['login'], payload['name']),
child: Container(
padding: EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Avatar(url: payload['owner']['avatarUrl'], size: 12),
SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: join(SizedBox(height: 8), <Widget>[
Row(
children: <Widget>[
Text(
payload['owner']['login'] + ' / ',
style: TextStyle(
fontSize: inRepoScreen ? 18 : 16,
color: PrimerColors.blue500,
fontWeight: FontWeight.w500,
),
),
),
Text(
payload['name'],
style: TextStyle(
fontSize: inRepoScreen ? 18 : 16,
color: PrimerColors.blue500,
fontWeight: FontWeight.w600,
Text(
payload['name'],
style: TextStyle(
fontSize: inRepoScreen ? 18 : 16,
color: PrimerColors.blue500,
fontWeight: FontWeight.w600,
),
),
),
],
),
if (payload['description'] != null &&
(payload['description'] as String).isNotEmpty)
Text(
payload['description'],
style: TextStyle(
color: PrimerColors.gray700,
fontSize: inRepoScreen ? 15 : 14),
],
),
if (inRepoScreen) _buildTopics() else _buildStatus(),
]),
if (payload['description'] != null &&
(payload['description'] as String).isNotEmpty)
Text(
payload['description'],
style: TextStyle(
color: PrimerColors.gray700,
fontSize: inRepoScreen ? 15 : 14),
),
if (inRepoScreen) _buildTopics() else _buildStatus(),
]),
),
),
),
Icon(_buildIconData(), size: 18, color: PrimerColors.gray600),
],
Icon(_buildIconData(), size: 18, color: PrimerColors.gray600),
],
),
),
);
if (inRepoScreen) {
return widget;
} else {
// TODO: text style
return Link(
screenBuilder: (_) =>
RepositoryScreen(payload['owner']['login'], payload['name']),
child: widget,
);
}
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/border_view.dart';
import 'package:primer/primer.dart';
import 'link.dart';
@ -102,9 +103,6 @@ class TableView extends StatelessWidget {
),
);
if (item.onTap == null && item.screenBuilder == null && item.url == null) {
return widget;
}
return Link(
onTap: item.onTap,
screenBuilder: item.screenBuilder,

View File

@ -40,59 +40,56 @@ class UserItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final widget = Container(
padding: EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Avatar(url: avatarUrl, size: 24),
SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
name ?? login,
style: TextStyle(
color: PrimerColors.blue500,
fontSize: inUserScreen ? 18 : 16,
fontWeight: FontWeight.w600,
return Link(
screenBuilder: inUserScreen
? null
: (_) =>
isOrganization ? OrganizationScreen(login) : UserScreen(login),
child: Container(
padding: EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Avatar(url: avatarUrl, size: 24),
SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text(
name ?? login,
style: TextStyle(
color: PrimerColors.blue500,
fontSize: inUserScreen ? 18 : 16,
fontWeight: FontWeight.w600,
),
),
),
SizedBox(width: 8),
Text(
login,
SizedBox(width: 8),
Text(
login,
style: TextStyle(
color: PrimerColors.gray700,
fontSize: inUserScreen ? 16 : 14),
),
],
),
SizedBox(height: 6),
if (bio != null && bio.isNotEmpty)
TextContainsOrganization(
bio,
style: TextStyle(
color: PrimerColors.gray700,
fontSize: inUserScreen ? 16 : 14),
fontSize: inUserScreen ? 15 : 14),
),
],
),
SizedBox(height: 6),
if (bio != null && bio.isNotEmpty)
TextContainsOrganization(
bio,
style: TextStyle(
color: PrimerColors.gray700,
fontSize: inUserScreen ? 15 : 14),
),
],
),
)
],
],
),
)
],
),
),
);
if (inUserScreen) {
return widget;
} else {
return Link(
screenBuilder: (_) =>
isOrganization ? OrganizationScreen(login) : UserScreen(login),
child: widget);
}
}
}