Instantly share code, notes, and snippets.
Created
July 10, 2020 21:14
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save lewismorgan/78af01df3d7f4c3e8e83c5f735580e98 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math'; | |
import 'package:flutter/cupertino.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter_bloc/flutter_bloc.dart'; | |
import 'package:vytality/api/models/post_model.dart'; | |
import 'package:vytality/presentation/bloc/post/post_bloc.dart'; | |
import 'package:vytality/presentation/widgets/comments/comments.dart'; | |
import 'package:vytality/presentation/widgets/common/user_avatar.dart'; | |
import 'package:vytality/presentation/widgets/feed/user_action.dart'; | |
/// The time it takes to expand a post | |
const Duration _kExpand = Duration(milliseconds: 350); | |
class Post extends StatefulWidget { | |
const Post({ | |
Key key, | |
}) : super(key: key); | |
@override | |
_PostState createState() => _PostState(); | |
} | |
class _PostState extends State<Post> { | |
PostBloc _bloc; | |
@override | |
void initState() { | |
super.initState(); | |
_bloc = BlocProvider.of<PostBloc>(context); | |
} | |
void _onDetailed(BuildContext context, PostModel post) { | |
Navigator.of(context).pushNamed('/post', arguments: {"model": post}); | |
} | |
void _onLike() { | |
// TODO: Display Reactions popovers | |
} | |
void _onComment() { | |
// TODO: Begin adding a new comment to the post | |
} | |
void _onShare(PostModel) { | |
// TODO: Display native Sharing dialog | |
// IDEAL: Direct links to the post, media and all | |
final state = _bloc.state; | |
if (state is PostItemState) { | |
final post = state.post; | |
} else if (state is PostInitial) {} | |
} | |
void _onOptions() { | |
// TODO: Add Post options | |
} | |
Widget _buildFromState(BuildContext context, PostItemState state) { | |
final textTheme = Theme.of(context).textTheme; | |
if (state.isLoading) { | |
return CircularProgressIndicator(); | |
} | |
final post = state.post; | |
return Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
// Avatar/Name, Post Timestamp | |
_buildHeader(context, state), | |
// User Action Information | |
if (post.hasAction) ...[ | |
Padding( | |
padding: const EdgeInsets.only(top: 12), | |
child: Center(child: UserActionActivity(action: post.action)), | |
), | |
] else ...[ | |
SizedBox(height: 23), | |
], | |
// Image | |
if (post.hasMedia) ...[ | |
Padding( | |
padding: const EdgeInsets.only(bottom: 12), | |
child: _buildMedia(post.mediaUrl), | |
), | |
], | |
// Post Body | |
Padding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 8, 8), | |
child: _buildBodyText(textTheme, state), | |
), | |
// Read More / Collapse | |
if (state.hasExpandableInfo && state.isCollapsed) ...[ | |
Padding( | |
padding: const EdgeInsets.only(top: 5, right: 25), | |
child: Align( | |
alignment: Alignment.topRight, | |
child: _buildSeeDetails(context, state), | |
), | |
), | |
], | |
// Action Bar | |
Padding( | |
padding: const EdgeInsets.only(top: 12), | |
child: _buildButtons(context, state), | |
), | |
// Reactions/Comments | |
if (post.hasReactions || post.hasComments) ...[ | |
Container( | |
padding: const EdgeInsets.only(top: 10), | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
children: [ | |
if (post.hasReactions) ...[ | |
_buildReactionsButton(textTheme, post), | |
] else ...[ | |
Spacer(), | |
], | |
if (post.hasComments) ...[ | |
_buildCommentsButton(textTheme, post), | |
], | |
], | |
), | |
), | |
if (!state.isCollapsed && state.hasExpandableInfo) ...[ | |
if (post.hasReactions) ...[ | |
Padding( | |
padding: const EdgeInsets.only(left: 20, top: 10), | |
child: _buildRecentReactions(post), | |
), | |
], | |
if (post.hasComments) ...[ | |
CommentsView(commentCount: post.commentCount), | |
], | |
], | |
], | |
], | |
); | |
} | |
Row _buildRecentReactions(PostModel post) { | |
final textTheme = Theme.of(context).textTheme; | |
return Row( | |
mainAxisSize: MainAxisSize.max, | |
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
children: [ | |
// Recent Reaction Avatars | |
Row( | |
children: List.generate( | |
post.recentReactors.length, | |
(index) => UserAvatar( | |
url: post.recentReactors[index].avatarUrl, | |
name: post.recentReactors[index].name, | |
), | |
), | |
), | |
// View all n reactions | |
// TODO: Display Extended Reactions dialog on View all reactions | |
Text( | |
'View all ${post.reactionCount} reactions', | |
style: textTheme.caption, | |
), | |
], | |
); | |
} | |
Widget _buildButton( | |
IconData icon, { | |
String label, | |
@required void Function() onTap, | |
bool flip = false, | |
PostItemState state, | |
}) { | |
var child; | |
if (flip) { | |
child = Transform( | |
alignment: Alignment.center, | |
transform: Matrix4.rotationY(pi), | |
child: Icon(icon, color: Colors.black, size: 32.0), | |
); | |
} else { | |
child = Icon(icon, color: Colors.black, size: 32.0); | |
} | |
return GestureDetector( | |
onTap: onTap, | |
behavior: HitTestBehavior.opaque, | |
child: label != null | |
? Row( | |
children: [ | |
child, | |
SizedBox(width: 5), | |
Text(label), | |
], | |
) | |
: child, | |
); | |
} | |
Widget _buildButtons(BuildContext context, PostItemState state) { | |
return Material( | |
elevation: 0, | |
color: Colors.white, | |
child: Container( | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: [ | |
_buildButton(CupertinoIcons.heart, label: 'Like', onTap: _onLike), | |
_buildButton(CupertinoIcons.conversation_bubble, | |
label: 'Comment', onTap: _onComment), | |
_buildButton(CupertinoIcons.reply, | |
label: 'Share', flip: true, onTap: _onShare), | |
], | |
), | |
), | |
); | |
} | |
Widget _buildBodyText(TextTheme textTheme, PostItemState state) { | |
String details; | |
if (state.hasExpandableInfo) { | |
details = state.visibleDetails; | |
} else { | |
details = state.post.details; | |
} | |
return AnimatedSwitcher( | |
duration: _kExpand, | |
child: Text( | |
details, | |
style: textTheme.bodyText2.copyWith(fontSize: 14.0), | |
), | |
); | |
} | |
Widget _buildSeeDetails(BuildContext context, PostItemState state) { | |
final textTheme = Theme.of(context).textTheme; | |
return GestureDetector( | |
behavior: HitTestBehavior.translucent, | |
onTap: () => _onDetailed(context, state.post), | |
child: Text( | |
'See More', | |
style: textTheme.caption, | |
), | |
); | |
} | |
Widget _buildCommentsButton(TextTheme text, PostModel post) { | |
return GestureDetector( | |
onTap: () => _onDetailed(context, post), | |
child: Text('${post.commentCount} Comments', style: text.caption), | |
); | |
} | |
Widget _buildReactionsButton(TextTheme text, PostModel post) { | |
return GestureDetector( | |
onTap: () => _onDetailed(context, post), | |
child: Text('${post.reactionCount} Reactions', style: text.caption), | |
); | |
} | |
Widget _buildHeader(BuildContext context, PostItemState state) { | |
final post = state.post; | |
return Row( | |
children: [ | |
UserAvatar( | |
radius: 25, url: post.author.avatarUrl, name: post.author.name), | |
Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 10), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.start, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
post.author.name, | |
style: Theme.of(context).textTheme.headline6.copyWith( | |
fontWeight: FontWeight.bold, | |
fontSize: 18, | |
), | |
), | |
Text( | |
state.displayedTime, | |
style: Theme.of(context).textTheme.caption, | |
) | |
], | |
), | |
), | |
Spacer(), | |
_buildButton(CupertinoIcons.ellipsis, onTap: _onOptions), | |
], | |
); | |
} | |
Widget _buildMedia(String url) { | |
return Align( | |
alignment: Alignment.center, | |
child: ClipRRect( | |
borderRadius: BorderRadius.circular(10), | |
child: Image.network( | |
url, | |
width: 375, | |
height: 295, | |
fit: BoxFit.cover, | |
), | |
), | |
); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return BlocBuilder<PostBloc, PostState>( | |
builder: (context, state) { | |
if (state is PostInitial) { | |
return CircularProgressIndicator(); | |
} | |
if (state is PostItemState) { | |
return _buildFromState(context, state); | |
} else { | |
return Container(); | |
} | |
}, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment