Merge branch 'feat/contacts-view-package' of beep/frontend_flutter into master
commit
704aab2ae7
|
@ -1,8 +1,9 @@
|
|||
import "package:flutter/services.dart";
|
||||
import 'routes.dart';
|
||||
import "src/blocs/heartbeat_bloc.dart";
|
||||
// import 'package:flutter/rendering.dart';
|
||||
|
||||
void main() {
|
||||
// debugPaintSizeEnabled = true;
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
|
||||
Routes();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'src/ui/home/home.dart';
|
||||
import 'src/ui/home.dart';
|
||||
import "src/ui/login/welcome.dart";
|
||||
import 'themer.dart';
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ class BottomBar extends StatelessWidget {
|
|||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(40.0), topRight: Radius.circular(40.0)),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 20.0, left: 20.0, right: 20.0, bottom: 30.0 + bottomPadding),
|
||||
padding:
|
||||
EdgeInsets.only(top: 20.0, left: 20.0, right: 20.0, bottom: 30.0),
|
||||
child: (conversationId == "")
|
||||
? ConversationInactiveView()
|
||||
: ConversationActiveView(conversationId: conversationId),
|
||||
|
|
|
@ -47,8 +47,8 @@ class _ConversationActiveViewState extends State<ConversationActiveView> {
|
|||
print(users[0].id);
|
||||
setState(() {
|
||||
_users = users
|
||||
.map((user) =>
|
||||
UserAvatar(padding: EdgeInsets.only(right: 5.0), user: user))
|
||||
.map((user) => UserAvatar(
|
||||
radius: 18.0, padding: EdgeInsets.only(right: 5.0), user: user))
|
||||
.toList();
|
||||
});
|
||||
});
|
||||
|
@ -67,8 +67,8 @@ class _ConversationActiveViewState extends State<ConversationActiveView> {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 22.0,
|
||||
height: 22.0,
|
||||
width: 15.0,
|
||||
height: 15.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).indicatorColor,
|
||||
shape: BoxShape.circle)),
|
||||
|
|
|
@ -13,12 +13,15 @@ class ConversationInactiveView extends StatelessWidget {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
UserAvatar(
|
||||
radius: 18,
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
user: User("1", "Isaac", "Tay", "+65 91043593")),
|
||||
UserAvatar(
|
||||
radius: 18,
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
user: User("1", "Isaac", "Tay", "+65 91043593")),
|
||||
UserAvatar(
|
||||
radius: 18,
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
user: User("1", "Isaac", "Tay", "+65 91043593"))
|
||||
])
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "./widgets/home_view.dart";
|
||||
|
||||
class ContactTab extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ContactTabState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactTabState extends State<ContactTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Navigator(
|
||||
initialRoute: "conversation/home",
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
WidgetBuilder builder;
|
||||
switch (settings.name) {
|
||||
case "conversation/home":
|
||||
builder = (BuildContext _) => HomeView();
|
||||
break;
|
||||
case "conversation/new":
|
||||
builder = (BuildContext _) => Center(child: Text("SOON"));
|
||||
break;
|
||||
default:
|
||||
throw Exception("Invalid route: ${settings.name}");
|
||||
}
|
||||
return MaterialPageRoute(builder: builder, settings: settings);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/user_model.dart";
|
||||
import "../../widgets/user_avatar.dart";
|
||||
|
||||
class ContactItem extends StatelessWidget {
|
||||
final User user;
|
||||
|
||||
ContactItem({@required this.user});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
UserAvatar(
|
||||
user: user,
|
||||
radius: 18.0,
|
||||
padding: EdgeInsets.only(left: 15.0)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 15.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(user.firstName + " " + user.lastName,
|
||||
style: Theme.of(context).textTheme.title,
|
||||
overflow: TextOverflow.ellipsis),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 2),
|
||||
child: Text("Last seen x ago",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle
|
||||
.copyWith(color: Color(0xFF455A64)))),
|
||||
]))
|
||||
]));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
import "package:flutter/material.dart";
|
||||
import 'package:sticky_headers/sticky_headers.dart';
|
||||
|
||||
import "../../../models/user_model.dart";
|
||||
import "../../../blocs/contact_bloc.dart";
|
||||
|
||||
import "../widgets/contact_item.dart";
|
||||
import "../../widgets/search_input.dart";
|
||||
|
||||
class HomeView extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _HomeViewState();
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeViewState extends State<HomeView> {
|
||||
final searchController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
contactBloc.fetchContacts();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: contactBloc.contacts,
|
||||
builder: (context, AsyncSnapshot<List<User>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return buildList(snapshot);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
return Center(child: CircularProgressIndicator());
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildList(AsyncSnapshot<List<User>> snapshot) {
|
||||
final Map<String, List<User>> sortedList = {
|
||||
"A": null,
|
||||
"B": null,
|
||||
"C": null,
|
||||
"D": null,
|
||||
"E": null,
|
||||
"F": null,
|
||||
"G": null,
|
||||
"H": null,
|
||||
"I": null,
|
||||
"J": null,
|
||||
"K": null,
|
||||
"L": null,
|
||||
"M": null,
|
||||
"N": null,
|
||||
"O": null,
|
||||
"P": null,
|
||||
"Q": null,
|
||||
"R": null,
|
||||
"S": null,
|
||||
"T": null,
|
||||
"U": null,
|
||||
"V": null,
|
||||
"W": null,
|
||||
"X": null,
|
||||
"Y": null,
|
||||
"Z": null
|
||||
};
|
||||
|
||||
// Sort the list into alphabets
|
||||
sortedList.forEach((letter, list) {
|
||||
sortedList[letter] = snapshot.data
|
||||
.where((user) => user.firstName.startsWith(letter))
|
||||
.toList();
|
||||
});
|
||||
|
||||
// Create list of children
|
||||
final children = sortedList.entries.map<Widget>((entry) {
|
||||
if (entry.value.length == 0) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
StickyHeader(
|
||||
header: Container(
|
||||
height: 20.0,
|
||||
color: Colors.grey[200],
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(entry.key,
|
||||
style: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display1
|
||||
.copyWith(color: Theme.of(context).primaryColorDark)),
|
||||
),
|
||||
content: ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(top: 0.0),
|
||||
shrinkWrap: true,
|
||||
itemCount: entry.value.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ContactItem(user: entry.value[index]);
|
||||
}))
|
||||
]);
|
||||
}).toList();
|
||||
|
||||
children.insertAll(0, [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 15.0, right: 15.0),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
SearchInput(
|
||||
controller: searchController, hintText: "Search for people"),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
|
||||
child: Row(children: <Widget>[
|
||||
Icon(Icons.people_outline,
|
||||
color: Theme.of(context).primaryColorDark, size: 30.0),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20.0),
|
||||
child: Text("Invite Friends",
|
||||
style: Theme.of(context).textTheme.title.copyWith(
|
||||
color: Theme.of(context).primaryColorDark))),
|
||||
])),
|
||||
])),
|
||||
]);
|
||||
return ListView(padding: EdgeInsets.only(top: 10.0), children: children);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "./widgets/home_view.dart";
|
||||
|
||||
class ConversationTab extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ConversationTabState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ConversationTabState extends State<ConversationTab> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Navigator(
|
||||
initialRoute: "conversation/home",
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
WidgetBuilder builder;
|
||||
switch (settings.name) {
|
||||
case "conversation/home":
|
||||
builder = (BuildContext _) => HomeView();
|
||||
break;
|
||||
case "conversation/new":
|
||||
builder = (BuildContext _) => Center(child: Text("SOON"));
|
||||
break;
|
||||
default:
|
||||
throw Exception("Invalid route: ${settings.name}");
|
||||
}
|
||||
return MaterialPageRoute(builder: builder, settings: settings);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/user_model.dart";
|
||||
import "../../../models/conversation_model.dart";
|
||||
import "../../../blocs/conversation_bloc.dart";
|
||||
import "../../../blocs/message_bloc.dart";
|
||||
|
||||
import "../../widgets/user_avatar.dart";
|
||||
|
||||
class ConversationItem extends StatefulWidget {
|
||||
final Conversation conversation;
|
||||
|
||||
ConversationItem({@required this.conversation});
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ConversationItemState(conversation: conversation);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConversationItemState extends State<ConversationItem> {
|
||||
final bloc;
|
||||
final Conversation conversation;
|
||||
final bus = messageBloc;
|
||||
|
||||
_ConversationItemState({@required this.conversation})
|
||||
: bloc = ConversationMembersBloc(conversation.id);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
bloc.fetchMembers();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
bloc.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
elevation: 1,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await bus.publish(
|
||||
{"state": "connect", "conversationId": conversation.id});
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 8.0, left: 10.0, right: 10.0, bottom: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
StreamBuilder(
|
||||
stream: bloc.members,
|
||||
builder:
|
||||
(context, AsyncSnapshot<List<User>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return avatarBuilder(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
return SizedBox(width: 18.0, height: 18.0);
|
||||
}),
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 10.0, right: 5.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(conversation.title,
|
||||
style:
|
||||
Theme.of(context).textTheme.title),
|
||||
Text("yaddaydaadadyasdhbsjdfhsbjdfhsbdug",
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle
|
||||
.copyWith(
|
||||
color: Color(0xFF455A64))),
|
||||
]))),
|
||||
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
Text("12:25 PM",
|
||||
style: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display2
|
||||
.copyWith(
|
||||
color: Theme.of(context).primaryColorDark)),
|
||||
])
|
||||
]))));
|
||||
}
|
||||
|
||||
Widget avatarBuilder(List<User> data) {
|
||||
if (data.length == 1) {
|
||||
return UserAvatar(radius: 25.0, user: data[0]);
|
||||
} else if (data.length > 1) {
|
||||
final groupUser = new User("0", conversation.title, "", "");
|
||||
return UserAvatar(radius: 25.0, user: groupUser);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/conversation_model.dart";
|
||||
import "../../../blocs/conversation_bloc.dart";
|
||||
|
||||
import "../widgets/conversation_item.dart";
|
||||
import "../../widgets/search_input.dart";
|
||||
|
||||
class HomeView extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _HomeViewState();
|
||||
}
|
||||
}
|
||||
|
||||
class _HomeViewState extends State<HomeView> {
|
||||
final searchController = TextEditingController();
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
conversationsBloc.fetchConversations();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(padding: EdgeInsets.only(top: 10.0), children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 10.0),
|
||||
child: SearchInput(
|
||||
controller: searchController,
|
||||
hintText: "Search for messages or users")),
|
||||
StreamBuilder(
|
||||
stream: conversationsBloc.conversations,
|
||||
builder: (context, AsyncSnapshot<List<Conversation>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return buildList(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
return Center(child: CircularProgressIndicator());
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
Widget buildList(List<Conversation> data) {
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.only(top: 0.0),
|
||||
shrinkWrap: true,
|
||||
itemCount: data.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ConversationItem(conversation: data[index]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
import "package:flutter/material.dart";
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import "./widgets/top_bar.dart";
|
||||
import "./conversation_tab/conversation_tab.dart";
|
||||
import "./contact_tab/contact_tab.dart";
|
||||
import "./bottom_bar/bottom_bar.dart";
|
||||
import "../services/heartbeat_manager.dart";
|
||||
import "../services/conversation_manager.dart";
|
||||
import "../blocs/message_bloc.dart";
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
@override
|
||||
_HomeState createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
final heartbeatSendManager = HeartbeatSendManager();
|
||||
final conversationManager = ConversationManager();
|
||||
|
||||
// Bottom Bar navigation
|
||||
int _tabNumber = 1;
|
||||
List<IconData> itemsList = [
|
||||
Icons.contacts,
|
||||
Icons.chat,
|
||||
Icons.settings,
|
||||
];
|
||||
TabController controller;
|
||||
|
||||
// Current conversaton
|
||||
String currentConversationId = "";
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
controller = TabController(vsync: this, length: 3);
|
||||
controller.index = 1; // Set default page to conversation page
|
||||
|
||||
messageBloc.bus.listen(
|
||||
(Map<String, String> data) async => await _processMessage(data));
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_processMessage(Map<String, String> data) async {
|
||||
if (data["state"] == "disconnect") {
|
||||
// Disconnect and change state
|
||||
await conversationManager.exit();
|
||||
setState(() {
|
||||
currentConversationId = "";
|
||||
});
|
||||
} else if (data["state"] == "connect") {
|
||||
// Connect and change state
|
||||
await conversationManager.join(data["conversationId"]);
|
||||
setState(() {
|
||||
currentConversationId = data["conversationId"];
|
||||
});
|
||||
} else {
|
||||
// show default
|
||||
await conversationManager.exit();
|
||||
setState(() {
|
||||
currentConversationId = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
body: Column(children: <Widget>[
|
||||
TopBar(state: _tabNumber),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
controller: controller,
|
||||
children: <Widget>[
|
||||
ContactTab(),
|
||||
ConversationTab(),
|
||||
Container(),
|
||||
])),
|
||||
]),
|
||||
bottomNavigationBar:
|
||||
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
BottomBar(conversationId: currentConversationId),
|
||||
BottomNavigationBar(
|
||||
onTap: (int index) {
|
||||
setState(() {
|
||||
_tabNumber = index;
|
||||
controller.index = _tabNumber;
|
||||
});
|
||||
},
|
||||
items: itemsList.map((data) {
|
||||
return BottomNavigationBarItem(
|
||||
icon: itemsList[_tabNumber] == data
|
||||
? ShaderMask(
|
||||
blendMode: BlendMode.srcIn,
|
||||
shaderCallback: (Rect bounds) {
|
||||
return ui.Gradient.linear(
|
||||
Offset(4.0, 24.0),
|
||||
Offset(24.0, 4.0),
|
||||
[
|
||||
Theme.of(context).primaryColor,
|
||||
Theme.of(context).primaryColorDark,
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Icon(data, size: 25.0),
|
||||
)
|
||||
: Icon(data, color: Colors.grey, size: 20),
|
||||
title: Container(),
|
||||
);
|
||||
}).toList())
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "./widgets/top_bar.dart";
|
||||
import "./widgets/conversation_list.dart";
|
||||
import "./widgets/contact_list.dart";
|
||||
import "../bottom_bar/bottom_bar.dart";
|
||||
import "../../services/heartbeat_manager.dart";
|
||||
import "../../services/conversation_manager.dart";
|
||||
import "../../blocs/message_bloc.dart";
|
||||
import "../../blocs/heartbeat_bloc.dart";
|
||||
|
||||
class Home extends StatefulWidget {
|
||||
@override
|
||||
_HomeState createState() => _HomeState();
|
||||
}
|
||||
|
||||
class _HomeState extends State<Home> {
|
||||
final List<String> titleList = ["Conversations", "Contacts", "Settings"];
|
||||
|
||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
final PageController controller = PageController();
|
||||
final heartbeatSendManager = HeartbeatSendManager();
|
||||
final conversationManager = ConversationManager();
|
||||
|
||||
int _pageNumber = 0;
|
||||
|
||||
PersistentBottomSheetController bottomBarController;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
controller.addListener(_updatePageNumber);
|
||||
|
||||
messageBloc.bus.listen(
|
||||
(Map<String, String> data) async => await _processMessage(data));
|
||||
_setupBottomState();
|
||||
}
|
||||
|
||||
@override
|
||||
dispose() {
|
||||
controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
_updatePageNumber() {
|
||||
setState(() {
|
||||
_pageNumber = controller.page.round();
|
||||
});
|
||||
}
|
||||
|
||||
_processMessage(Map<String, String> data) async {
|
||||
if (data["state"] == "disconnect") {
|
||||
// Disconnect and change state
|
||||
await conversationManager.exit();
|
||||
bottomBarController.close();
|
||||
bottomBarController = _scaffoldKey.currentState.showBottomSheet<void>(
|
||||
(BuildContext context) => BottomBar(conversationId: ""));
|
||||
} else if (data["state"] == "connect") {
|
||||
// Connect and change state
|
||||
await conversationManager.join(data["conversationId"]);
|
||||
bottomBarController.close();
|
||||
bottomBarController = _scaffoldKey.currentState.showBottomSheet<void>(
|
||||
(BuildContext context) =>
|
||||
BottomBar(conversationId: data["conversationId"]));
|
||||
} else {
|
||||
// show default
|
||||
await conversationManager.exit();
|
||||
bottomBarController.close();
|
||||
bottomBarController = _scaffoldKey.currentState.showBottomSheet<void>(
|
||||
(BuildContext context) => BottomBar(conversationId: ""));
|
||||
}
|
||||
}
|
||||
|
||||
_setupBottomState() {
|
||||
conversationManager.get().then((conversationId) {
|
||||
bottomBarController = _scaffoldKey.currentState.showBottomSheet<void>(
|
||||
(BuildContext context) => BottomBar(conversationId: conversationId));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
key: _scaffoldKey,
|
||||
body: Column(children: <Widget>[
|
||||
TopBar(title: titleList[_pageNumber], pageNumber: _pageNumber),
|
||||
Expanded(
|
||||
child: PageView(controller: controller, children: <Widget>[
|
||||
ConversationList(),
|
||||
ContactList(),
|
||||
])),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/user_model.dart";
|
||||
import "../../widgets/user_avatar.dart";
|
||||
|
||||
class ContactItem extends StatelessWidget {
|
||||
final User user;
|
||||
|
||||
ContactItem({@required this.user});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
contentPadding:
|
||||
EdgeInsets.only(top: 0.0, left: 20.0, right: 20.0, bottom: 0.0),
|
||||
leading: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||||
/*Icon(Icons.star, color: Theme.of(context).primaryColorDark),*/
|
||||
Text("A",
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).primaryColorDark)),
|
||||
UserAvatar(
|
||||
user: user, radius: 22.0, padding: EdgeInsets.only(left: 20.0))
|
||||
]),
|
||||
title: Text(user.firstName + " " + user.lastName,
|
||||
style: Theme.of(context).textTheme.display2,
|
||||
overflow: TextOverflow.ellipsis),
|
||||
subtitle: Text("Last seen just now",
|
||||
style: Theme.of(context).textTheme.subtitle),
|
||||
onTap: () => {});
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/user_model.dart";
|
||||
import "../../../blocs/contact_bloc.dart";
|
||||
|
||||
import "contact_item.dart";
|
||||
|
||||
class ContactList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ContactListState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactListState extends State<ContactList> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
contactBloc.fetchContacts();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StreamBuilder(
|
||||
stream: contactBloc.contacts,
|
||||
builder: (context, AsyncSnapshot<List<User>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return buildList(snapshot);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
return Center(child: CircularProgressIndicator());
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildList(AsyncSnapshot<List<User>> snapshot) {
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.only(top: 0.0),
|
||||
itemCount: snapshot.data.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ContactItem(user: snapshot.data[index]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/user_model.dart";
|
||||
import "../../../models/conversation_model.dart";
|
||||
import "../../../blocs/conversation_bloc.dart";
|
||||
import "../../../blocs/message_bloc.dart";
|
||||
|
||||
import "../../widgets/user_avatar.dart";
|
||||
|
||||
class ConversationItem extends StatefulWidget {
|
||||
final Conversation conversation;
|
||||
|
||||
ConversationItem({@required this.conversation});
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ConversationItemState(conversation: conversation);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConversationItemState extends State<ConversationItem> {
|
||||
final bloc;
|
||||
final Conversation conversation;
|
||||
final bus = messageBloc;
|
||||
|
||||
_ConversationItemState({@required this.conversation})
|
||||
: bloc = ConversationMembersBloc(conversation.id);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
bloc.fetchMembers();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
bloc.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
elevation: 0,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await bus.publish(
|
||||
{"state": "connect", "conversationId": conversation.id});
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(
|
||||
top: 5.0, left: 20.0, right: 20.0, bottom: 5.0),
|
||||
child: Column(children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(conversation.title,
|
||||
style: Theme.of(context).textTheme.title),
|
||||
Spacer(),
|
||||
Text("1m ago",
|
||||
style: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.body1
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Theme.of(context).primaryColorDark)),
|
||||
]),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 5.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
"I might have forgotten to close the windows",
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle)),
|
||||
StreamBuilder(
|
||||
stream: bloc.members,
|
||||
builder: (context,
|
||||
AsyncSnapshot<List<User>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return membersBuilder(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
return SizedBox(width: 18.0, height: 18.0);
|
||||
}),
|
||||
]))
|
||||
]))));
|
||||
}
|
||||
|
||||
Widget membersBuilder(List<User> data) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: data
|
||||
.map((user) => UserAvatar(
|
||||
radius: 18.0,
|
||||
padding: EdgeInsets.only(top: 0.0, left: 5.0),
|
||||
user: user))
|
||||
.toList());
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "../../../models/conversation_model.dart";
|
||||
import "../../../blocs/conversation_bloc.dart";
|
||||
|
||||
import "conversation_item.dart";
|
||||
|
||||
class ConversationList extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _ConversationListState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ConversationListState extends State<ConversationList> {
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
conversationsBloc.fetchConversations();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 10.0),
|
||||
child: StreamBuilder(
|
||||
stream: conversationsBloc.conversations,
|
||||
builder: (context, AsyncSnapshot<List<Conversation>> snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return buildList(snapshot.data);
|
||||
} else if (snapshot.hasError) {
|
||||
return Text(snapshot.error.toString());
|
||||
}
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}));
|
||||
}
|
||||
|
||||
Widget buildList(List<Conversation> data) {
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.only(top: 0.0),
|
||||
itemCount: data.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ConversationItem(conversation: data[index]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
class TopBar extends StatelessWidget {
|
||||
final String title;
|
||||
final int pageNumber;
|
||||
final double barHeight = 100.0;
|
||||
final String logo = "assets/logo.png";
|
||||
|
||||
TopBar({@required this.title, @required this.pageNumber});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double statusbarHeight = MediaQuery.of(context).padding.top;
|
||||
|
||||
// TODO: Fix cropping by moving onto stack, refactor widget into smaller parts
|
||||
return Material(
|
||||
type: MaterialType.canvas,
|
||||
elevation: 10.0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: statusbarHeight, bottom: 10.0),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
elevation: 10.0,
|
||||
color: Colors.transparent,
|
||||
child: Column(children: <Widget>[
|
||||
Stack(alignment: Alignment.center, children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 10.0),
|
||||
child: Image.asset(logo,
|
||||
semanticLabel: "Beep Logo",
|
||||
width: 24.0,
|
||||
height: 24.0)),
|
||||
Spacer(),
|
||||
IconButton(icon: Icon(Icons.search), onPressed: () {}),
|
||||
IconButton(
|
||||
icon: Icon(Icons.add_comment), onPressed: () {})
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
child: Text(title,
|
||||
style: Theme.of(context).accentTextTheme.display1)),
|
||||
]),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Opacity(
|
||||
opacity: (pageNumber == 0) ? 1.0 : 0.6,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 5.0),
|
||||
child: Container(
|
||||
width: 5.0,
|
||||
height: 5.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle)))),
|
||||
Opacity(
|
||||
opacity: (pageNumber == 1) ? 1.0 : 0.6,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 5.0),
|
||||
child: Container(
|
||||
width: 5.0,
|
||||
height: 5.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle)))),
|
||||
Opacity(
|
||||
opacity: (pageNumber == 2) ? 1.0 : 0.6,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 5.0),
|
||||
child: Container(
|
||||
width: 5.0,
|
||||
height: 5.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle)))),
|
||||
]),
|
||||
])),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).primaryColor,
|
||||
Theme.of(context).primaryColorDark
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
stops: [0.0, 1.0],
|
||||
tileMode: TileMode.clamp),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -41,12 +41,11 @@ class _LoginPageState extends State<LoginPage> {
|
|||
Text("First things first.",
|
||||
textAlign: TextAlign.left,
|
||||
style: Theme.of(context).accentTextTheme.display3),
|
||||
Text(
|
||||
"Enter your phone number, to connect to your existing Beep account.",
|
||||
style: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.copyWith(fontWeight: FontWeight.w400)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 5.0),
|
||||
child: Text(
|
||||
"Enter your phone number, to connect to your existing Beep account.",
|
||||
style: Theme.of(context).accentTextTheme.title)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: PhoneInput(controller: controller)),
|
||||
|
|
|
@ -36,12 +36,11 @@ class _OtpPageState extends State<OtpPage> {
|
|||
Text("Almost there.",
|
||||
textAlign: TextAlign.left,
|
||||
style: Theme.of(context).accentTextTheme.display3),
|
||||
Text(
|
||||
"I've sent an authentication code via SMS to your device, enter it below.",
|
||||
style: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.copyWith(fontWeight: FontWeight.w400)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 5.0),
|
||||
child: Text(
|
||||
"I've sent an authentication code via SMS to your device, enter it below.",
|
||||
style: Theme.of(context).accentTextTheme.title)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20.0),
|
||||
child: Center(
|
||||
|
|
|
@ -15,7 +15,7 @@ class PhoneInput extends StatelessWidget {
|
|||
width: 45,
|
||||
child: Center(
|
||||
child: Text("+65",
|
||||
style: Theme.of(context).textTheme.body2.copyWith(
|
||||
style: Theme.of(context).textTheme.title.copyWith(
|
||||
color: Theme.of(context).primaryColorDark))),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
class SearchInput extends StatelessWidget {
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
|
||||
SearchInput({@required this.controller, @required this.hintText});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
child: Icon(Icons.search, color: Colors.grey[500])),
|
||||
Flexible(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
autocorrect: false,
|
||||
cursorWidth: 2.0,
|
||||
cursorColor: Colors.grey[500],
|
||||
style: Theme.of(context).textTheme.subtitle.copyWith(
|
||||
color: Colors.grey[500],
|
||||
fontWeight: FontWeight.w300),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
filled: false,
|
||||
hintText: hintText,
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.subtitle
|
||||
.copyWith(color: Colors.grey[500])))),
|
||||
])),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[200],
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.00)),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import "package:flutter/material.dart";
|
||||
|
||||
import "search_input.dart";
|
||||
|
||||
class TopBar extends StatelessWidget {
|
||||
final int state;
|
||||
final String logo = "assets/logo.png";
|
||||
final List<String> titleList = ["Contacts", "Conversations", "Settings"];
|
||||
|
||||
TopBar({@required this.state});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double statusbarHeight = MediaQuery.of(context).padding.top;
|
||||
|
||||
return Material(
|
||||
type: MaterialType.canvas,
|
||||
elevation: 5.0,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: statusbarHeight, bottom: 0),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
elevation: 0.0,
|
||||
color: Colors.transparent,
|
||||
child: Column(children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 13.0),
|
||||
child: Text("Edit",
|
||||
style: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.copyWith(fontWeight: FontWeight.w300))),
|
||||
Spacer(),
|
||||
Text(titleList[state],
|
||||
style: Theme.of(context).accentTextTheme.display1),
|
||||
Spacer(),
|
||||
(state == 1)
|
||||
? IconButton(
|
||||
icon: Icon(Icons.add_comment), onPressed: () {})
|
||||
: Container(),
|
||||
(state == 0)
|
||||
? IconButton(icon: Icon(Icons.add), onPressed: () {})
|
||||
: Container(),
|
||||
(state == 2)
|
||||
? Opacity(
|
||||
opacity: 0.0,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.edit), onPressed: () {}))
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
])),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).primaryColor,
|
||||
Theme.of(context).primaryColorDark
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
stops: [0.0, 1.0],
|
||||
tileMode: TileMode.clamp),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import "package:flutter/material.dart";
|
||||
import 'dart:ui' as ui;
|
||||
import "dart:async";
|
||||
|
||||
import "../../models/user_model.dart";
|
||||
|
@ -42,19 +43,37 @@ class _UserAvatarState extends State<UserAvatar> {
|
|||
String lastName =
|
||||
(widget.user.lastName.isEmpty) ? '' : widget.user.lastName[0];
|
||||
|
||||
final colors = _stringToColor(widget.user.lastName);
|
||||
|
||||
return Padding(
|
||||
padding: widget.padding,
|
||||
child: Stack(alignment: Alignment.bottomRight, children: <Widget>[
|
||||
CircleAvatar(
|
||||
backgroundColor: _stringToColor(widget.user.lastName),
|
||||
child: Text(
|
||||
firstName.toUpperCase() + lastName.toUpperCase(),
|
||||
style: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.copyWith(fontSize: widget.radius / 1.2),
|
||||
),
|
||||
radius: widget.radius),
|
||||
Container(
|
||||
height: (widget.radius * 2),
|
||||
width: (widget.radius * 2),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
stops: [
|
||||
0,
|
||||
1
|
||||
],
|
||||
colors: [
|
||||
colors[0],
|
||||
colors[1],
|
||||
]),
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(widget.radius))),
|
||||
child: Center(
|
||||
child: Text(
|
||||
firstName.toUpperCase() + lastName.toUpperCase(),
|
||||
style: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.copyWith(fontSize: widget.radius / 1.4),
|
||||
),
|
||||
)),
|
||||
StreamBuilder(
|
||||
stream: heartbeatReceiverBloc.stream,
|
||||
builder: (context, AsyncSnapshot<Map<String, String>> snapshot) {
|
||||
|
@ -84,7 +103,7 @@ class _UserAvatarState extends State<UserAvatar> {
|
|||
}
|
||||
|
||||
// Hashing username into a pastel color
|
||||
Color _stringToColor(String str) {
|
||||
List<Color> _stringToColor(String str) {
|
||||
int hash = 0;
|
||||
|
||||
str.runes.forEach((int rune) {
|
||||
|
@ -93,6 +112,9 @@ class _UserAvatarState extends State<UserAvatar> {
|
|||
|
||||
hash = hash % 360;
|
||||
|
||||
return HSLColor.fromAHSL(1.0, hash.toDouble(), 0.8, 0.4).toColor();
|
||||
return [
|
||||
HSLColor.fromAHSL(1.0, hash.toDouble(), 0.8, 0.4).toColor(),
|
||||
HSLColor.fromAHSL(1.0, hash.toDouble(), 0.8, 0.5).toColor()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,16 +31,17 @@ TextTheme buildTextTheme(TextTheme base) {
|
|||
.copyWith(fontSize: 40.0, fontWeight: FontWeight.w600),
|
||||
display3: base.display3
|
||||
.copyWith(fontSize: 30.0, fontWeight: FontWeight.w700),
|
||||
display2: base.display2
|
||||
.copyWith(fontSize: 18.0, fontWeight: FontWeight.w500),
|
||||
display1: base.display1
|
||||
.copyWith(fontSize: 19.0, fontWeight: FontWeight.w600),
|
||||
display2: base.display2.copyWith(
|
||||
fontSize: 12.0, fontWeight: FontWeight.w500), // Used for time
|
||||
display1: base.display1.copyWith(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w600), // Used for overall title
|
||||
title:
|
||||
base.title.copyWith(fontSize: 18.0, fontWeight: FontWeight.w500),
|
||||
base.title.copyWith(fontSize: 16.0, fontWeight: FontWeight.w500),
|
||||
subtitle: base.subtitle
|
||||
.copyWith(fontSize: 12.0, fontWeight: FontWeight.w300),
|
||||
body2:
|
||||
base.body2.copyWith(fontSize: 16.0, fontWeight: FontWeight.w600),
|
||||
.copyWith(fontSize: 13.0, fontWeight: FontWeight.w300),
|
||||
body2: base.body2.copyWith(
|
||||
fontSize: 12.0, fontWeight: FontWeight.w600), // Bold normal
|
||||
body1:
|
||||
base.body1.copyWith(fontSize: 12.0, fontWeight: FontWeight.w400))
|
||||
.apply(
|
||||
|
|
|
@ -16,6 +16,7 @@ dependencies:
|
|||
shared_preferences: ^0.5.1
|
||||
sqflite: ^1.1.0
|
||||
eventsource: ^0.2.1
|
||||
sticky_headers: ^0.1.7
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue