4
2
Fork 0

feat: adding some new UI widgets, search bar, and tab navigation

pull/53/head
Sudharshan S. 2019-06-13 16:00:55 +08:00
parent 2b3891f2fe
commit f8acdeb4f3
Signed by: sudharshan
GPG Key ID: C861C97AAF3D9559
9 changed files with 202 additions and 156 deletions

View File

@ -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),

View File

@ -1,39 +1,45 @@
import "package:flutter/material.dart";
import 'dart:ui' as ui;
import "./widgets/top_bar.dart";
import "./widgets/conversation_list.dart";
import "./widgets/contact_list.dart";
import "./widgets/conversation_view.dart";
import "./widgets/contact_view.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"];
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final PageController controller = PageController();
final heartbeatSendManager = HeartbeatSendManager();
final conversationManager = ConversationManager();
int _pageNumber = 0;
// Bottom Bar navigation
final List<String> titleList = ["Contacts", "Conversations", "Settings"];
int _tabNumber = 1;
List<IconData> itemsList = [
Icons.contacts,
Icons.chat,
Icons.settings,
];
TabController controller;
PersistentBottomSheetController bottomBarController;
// Current conversaton
String currentConversationId = "";
@override
initState() {
super.initState();
controller.addListener(_updatePageNumber);
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));
_setupBottomState();
}
@override
@ -42,53 +48,75 @@ class _HomeState extends State<Home> {
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: ""));
setState(() {
currentConversationId = "";
});
} 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"]));
setState(() {
currentConversationId = data["conversationId"];
});
} else {
// show default
await conversationManager.exit();
bottomBarController.close();
bottomBarController = _scaffoldKey.currentState.showBottomSheet<void>(
(BuildContext context) => BottomBar(conversationId: ""));
setState(() {
currentConversationId = "";
});
}
}
_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),
TopBar(title: titleList[_tabNumber]),
Expanded(
child: PageView(controller: controller, children: <Widget>[
ConversationList(),
ContactList(),
])),
child: TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: controller,
children: <Widget>[
ContactView(),
ConversationView(),
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),
)
: Icon(data, color: Colors.grey),
title: Container(),
);
}).toList())
]),
);
}

View File

@ -5,14 +5,14 @@ import "../../../blocs/contact_bloc.dart";
import "contact_item.dart";
class ContactList extends StatefulWidget {
class ContactView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ContactListState();
return _ContactViewState();
}
}
class _ContactListState extends State<ContactList> {
class _ContactViewState extends State<ContactView> {
@override
void initState() {
super.initState();

View File

@ -57,12 +57,12 @@ class _ConversationItemState extends State<ConversationItem> {
Text(conversation.title,
style: Theme.of(context).textTheme.title),
Spacer(),
Text("1m ago",
Text("12:25 PM",
style: Theme.of(context)
.primaryTextTheme
.body1
.copyWith(
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryColorDark)),
]),
Padding(

View File

@ -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]);
},
);
}
}

View File

@ -0,0 +1,65 @@
import "package:flutter/material.dart";
import "../../../models/conversation_model.dart";
import "../../../blocs/conversation_bloc.dart";
import "conversation_item.dart";
import "search_input.dart";
class ConversationView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ConversationViewState();
}
}
class _ConversationViewState extends State<ConversationView> {
final searchController = TextEditingController();
@override
initState() {
super.initState();
conversationsBloc.fetchConversations();
}
@override
dispose() {
searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 10.0),
child: Column(mainAxisSize: MainAxisSize.min, 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")),
Flexible(
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]);
},
);
}
}

View File

@ -0,0 +1,41 @@
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.body2.copyWith(
color: Colors.grey[500], fontWeight: FontWeight.w300),
decoration: InputDecoration(
border: InputBorder.none,
filled: false,
hintText: hintText,
hintStyle: Theme.of(context).textTheme.body2.copyWith(
color: Colors.grey[500],
fontWeight: FontWeight.w300,
)))),
])),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.all(Radius.circular(10.00)),
));
}
}

View File

@ -2,83 +2,42 @@ 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});
TopBar({@required this.title});
@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,
elevation: 5.0,
child: Container(
padding: EdgeInsets.only(top: statusbarHeight, bottom: 10.0),
padding: EdgeInsets.only(top: statusbarHeight, bottom: 2.0),
child: Material(
type: MaterialType.transparency,
elevation: 10.0,
elevation: 0.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)))),
]),
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 13.0),
child: Text("Edit",
style: Theme.of(context)
.accentTextTheme
.display2
.copyWith(fontWeight: FontWeight.w400))),
Spacer(),
Text(title,
style: Theme.of(context).accentTextTheme.display1),
Spacer(),
IconButton(icon: Icon(Icons.add_comment), onPressed: () {})
],
),
])),
decoration: BoxDecoration(
gradient: LinearGradient(

View File

@ -38,7 +38,7 @@ TextTheme buildTextTheme(TextTheme base) {
title:
base.title.copyWith(fontSize: 18.0, fontWeight: FontWeight.w500),
subtitle: base.subtitle
.copyWith(fontSize: 12.0, fontWeight: FontWeight.w300),
.copyWith(fontSize: 14.0, fontWeight: FontWeight.w300),
body2:
base.body2.copyWith(fontSize: 16.0, fontWeight: FontWeight.w600),
body1: