mirror of
https://github.com/mdanics/fluttergram.git
synced 2025-08-06 13:19:53 +08:00
485 lines
15 KiB
Dart
485 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'main.dart';
|
|
import 'image_post.dart';
|
|
import 'dart:async';
|
|
import 'edit_profile_page.dart';
|
|
|
|
class ProfilePage extends StatefulWidget {
|
|
const ProfilePage({this.userId});
|
|
|
|
final String userId;
|
|
|
|
_ProfilePage createState() => new _ProfilePage(this.userId);
|
|
}
|
|
|
|
class _ProfilePage extends State<ProfilePage> with AutomaticKeepAliveClientMixin<ProfilePage> {
|
|
final String profileId;
|
|
String currentUserId = googleSignIn.currentUser.id;
|
|
String view = "grid"; // default view
|
|
bool isFollowing = false;
|
|
bool followButtonClicked = false;
|
|
int postCount = 0;
|
|
int followerCount = 0;
|
|
int followingCount = 0;
|
|
_ProfilePage(this.profileId);
|
|
|
|
editProfile() {
|
|
EditProfilePage editPage = new EditProfilePage();
|
|
|
|
Navigator.of(context)
|
|
.push(new MaterialPageRoute<bool>(builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Scaffold(
|
|
appBar: new AppBar(
|
|
leading: new IconButton(
|
|
icon: new Icon(Icons.close),
|
|
onPressed: () {
|
|
Navigator.maybePop(context);
|
|
},
|
|
),
|
|
title: new Text('Edit Profile',
|
|
style: new TextStyle(
|
|
color: Colors.black, fontWeight: FontWeight.bold)),
|
|
backgroundColor: Colors.white,
|
|
actions: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(
|
|
Icons.check,
|
|
color: Colors.blueAccent,
|
|
),
|
|
onPressed: () {
|
|
editPage.applyChanges();
|
|
Navigator.maybePop(context);
|
|
})
|
|
],
|
|
),
|
|
body: new ListView(
|
|
children: <Widget>[
|
|
new Container(
|
|
child: editPage,
|
|
),
|
|
],
|
|
)),
|
|
);
|
|
}));
|
|
}
|
|
|
|
followUser() {
|
|
print('following user');
|
|
setState(() {
|
|
this.isFollowing = true;
|
|
followButtonClicked = true;
|
|
});
|
|
|
|
Firestore.instance.document("insta_users/$profileId").updateData({
|
|
'followers.$currentUserId': true
|
|
//firestore plugin doesnt support deleting, so it must be nulled / falsed
|
|
});
|
|
|
|
Firestore.instance.document("insta_users/$currentUserId").updateData({
|
|
'following.$profileId': true
|
|
//firestore plugin doesnt support deleting, so it must be nulled / falsed
|
|
});
|
|
|
|
//updates activity feed
|
|
Firestore.instance
|
|
.collection("insta_a_feed")
|
|
.document(profileId)
|
|
.collection("items")
|
|
.document(currentUserId)
|
|
.setData({
|
|
"ownerId": profileId,
|
|
"username": currentUserModel.username,
|
|
"userId": currentUserId,
|
|
"type": "follow",
|
|
"userProfileImg": currentUserModel.photoUrl,
|
|
"timestamp": new DateTime.now().toString()
|
|
});
|
|
}
|
|
|
|
unfollowUser() {
|
|
setState(() {
|
|
isFollowing = false;
|
|
followButtonClicked = true;
|
|
});
|
|
|
|
Firestore.instance.document("insta_users/$profileId").updateData({
|
|
'followers.$currentUserId': false
|
|
//firestore plugin doesnt support deleting, so it must be nulled / falsed
|
|
});
|
|
|
|
Firestore.instance.document("insta_users/$currentUserId").updateData({
|
|
'following.$profileId': false
|
|
//firestore plugin doesnt support deleting, so it must be nulled / falsed
|
|
});
|
|
|
|
Firestore.instance
|
|
.collection("insta_a_feed")
|
|
.document(profileId)
|
|
.collection("items")
|
|
.document(currentUserId)
|
|
.delete();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
super.build(context); // reloads state when opened again
|
|
|
|
Column buildStatColumn(String label, int number) {
|
|
return new Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: <Widget>[
|
|
new Text(
|
|
number.toString(),
|
|
style: new TextStyle(fontSize: 22.0, fontWeight: FontWeight.bold),
|
|
),
|
|
new Container(
|
|
margin: const EdgeInsets.only(top: 4.0),
|
|
child: new Text(
|
|
label,
|
|
style: new TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 15.0,
|
|
fontWeight: FontWeight.w400),
|
|
))
|
|
],
|
|
);
|
|
}
|
|
|
|
Container buildFollowButton(
|
|
{String text,
|
|
Color backgroundcolor,
|
|
Color textColor,
|
|
Color borderColor,
|
|
Function function}) {
|
|
return new Container(
|
|
padding: const EdgeInsets.only(top: 2.0),
|
|
child: new FlatButton(
|
|
onPressed: function,
|
|
child: new Container(
|
|
decoration: new BoxDecoration(
|
|
color: backgroundcolor,
|
|
border: new Border.all(color: borderColor),
|
|
borderRadius: new BorderRadius.circular(5.0)),
|
|
alignment: Alignment.center,
|
|
child: new Text(text,
|
|
style: new TextStyle(
|
|
color: textColor, fontWeight: FontWeight.bold)),
|
|
width: 250.0,
|
|
height: 27.0,
|
|
)),
|
|
);
|
|
}
|
|
|
|
Container buildProfileFollowButton(User user) {
|
|
// viewing your own profile - should show edit button
|
|
if (currentUserId == profileId) {
|
|
return buildFollowButton(
|
|
text: "Edit Profile",
|
|
backgroundcolor: Colors.white,
|
|
textColor: Colors.black,
|
|
borderColor: Colors.grey,
|
|
function: editProfile,
|
|
);
|
|
}
|
|
|
|
// already following user - should show unfollow button
|
|
if (isFollowing) {
|
|
return buildFollowButton(
|
|
text: "Unfollow",
|
|
backgroundcolor: Colors.white,
|
|
textColor: Colors.black,
|
|
borderColor: Colors.grey,
|
|
function: unfollowUser,
|
|
);
|
|
}
|
|
|
|
// does not follow user - should show follow button
|
|
if (!isFollowing) {
|
|
return buildFollowButton(
|
|
text: "Follow",
|
|
backgroundcolor: Colors.blue,
|
|
textColor: Colors.white,
|
|
borderColor: Colors.blue,
|
|
function: followUser,
|
|
);
|
|
}
|
|
|
|
return buildFollowButton(
|
|
text: "loading...",
|
|
backgroundcolor: Colors.white,
|
|
textColor: Colors.black,
|
|
borderColor: Colors.grey);
|
|
}
|
|
|
|
Row buildImageViewButtonBar() {
|
|
Color isActiveButtonColor(String viewName) {
|
|
if (view == viewName) {
|
|
return Colors.blueAccent;
|
|
} else {
|
|
return Colors.black26;
|
|
}
|
|
}
|
|
|
|
return new Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
children: <Widget>[
|
|
new IconButton(
|
|
icon: new Icon(Icons.grid_on, color: isActiveButtonColor("grid")),
|
|
onPressed: () {
|
|
changeView("grid");
|
|
},
|
|
),
|
|
new IconButton(
|
|
icon: new Icon(Icons.list, color: isActiveButtonColor("feed")),
|
|
onPressed: () {
|
|
changeView("feed");
|
|
},
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Container buildUserPosts() {
|
|
Future<List<ImagePost>> getPosts() async {
|
|
List<ImagePost> posts = [];
|
|
var snap = await Firestore.instance
|
|
.collection('insta_posts')
|
|
.where('ownerId', isEqualTo: profileId)
|
|
.orderBy("timestamp")
|
|
.getDocuments();
|
|
for (var doc in snap.documents) {
|
|
posts.add(new ImagePost.fromDocument(doc));
|
|
}
|
|
setState(() {
|
|
postCount = snap.documents.length;
|
|
});
|
|
|
|
return posts.reversed.toList();
|
|
}
|
|
|
|
return new Container(
|
|
child: new FutureBuilder<List<ImagePost>>(
|
|
future: getPosts(),
|
|
builder: (context, snapshot) {
|
|
if (!snapshot.hasData)
|
|
return new Container(
|
|
alignment: FractionalOffset.center,
|
|
padding: const EdgeInsets.only(top: 10.0),
|
|
child: new CircularProgressIndicator());
|
|
else if (view == "grid") {
|
|
// build the grid
|
|
return new GridView.count(
|
|
crossAxisCount: 3,
|
|
childAspectRatio: 1.0,
|
|
// padding: const EdgeInsets.all(0.5),
|
|
mainAxisSpacing: 1.5,
|
|
crossAxisSpacing: 1.5,
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
children: snapshot.data.map((ImagePost imagePost) {
|
|
return new GridTile(child: new ImageTile(imagePost));
|
|
}).toList());
|
|
} else if (view == "feed") {
|
|
return new Column(
|
|
children: snapshot.data.map((ImagePost imagePost) {
|
|
return imagePost;
|
|
}).toList());
|
|
}
|
|
},
|
|
));
|
|
}
|
|
|
|
return new StreamBuilder(
|
|
stream: Firestore.instance
|
|
.collection('insta_users')
|
|
.document(profileId)
|
|
.snapshots(),
|
|
builder: (context, snapshot) {
|
|
if (!snapshot.hasData)
|
|
return new Container(
|
|
alignment: FractionalOffset.center,
|
|
child: new CircularProgressIndicator());
|
|
|
|
User user = new User.fromDocument(snapshot.data);
|
|
|
|
if (user.followers.containsKey(currentUserId) &&
|
|
user.followers[currentUserId] &&
|
|
followButtonClicked == false) {
|
|
isFollowing = true;
|
|
}
|
|
|
|
return new Scaffold(
|
|
appBar: new AppBar(
|
|
title: new Text(
|
|
user.username,
|
|
style: const TextStyle(color: Colors.black),
|
|
),
|
|
backgroundColor: Colors.white,
|
|
),
|
|
body: new ListView(
|
|
children: <Widget>[
|
|
new Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new Row(
|
|
children: <Widget>[
|
|
new CircleAvatar(
|
|
radius: 40.0,
|
|
backgroundColor: Colors.grey,
|
|
backgroundImage: new NetworkImage(user.photoUrl),
|
|
),
|
|
new Expanded(
|
|
flex: 1,
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new Row(
|
|
mainAxisSize: MainAxisSize.max,
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceEvenly,
|
|
children: <Widget>[
|
|
buildStatColumn("posts", postCount),
|
|
buildStatColumn("followers",
|
|
_countFollowings(user.followers)),
|
|
buildStatColumn("following",
|
|
_countFollowings(user.following)),
|
|
],
|
|
),
|
|
new Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceEvenly,
|
|
children: <Widget>[
|
|
buildProfileFollowButton(user)
|
|
]),
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
new Container(
|
|
alignment: Alignment.centerLeft,
|
|
padding: const EdgeInsets.only(top: 15.0),
|
|
child: new Text(
|
|
user.displayName,
|
|
style: new TextStyle(fontWeight: FontWeight.bold),
|
|
)),
|
|
new Container(
|
|
alignment: Alignment.centerLeft,
|
|
padding: const EdgeInsets.only(top: 1.0),
|
|
child: new Text(user.bio),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
new Divider(),
|
|
buildImageViewButtonBar(),
|
|
new Divider(height: 0.0),
|
|
buildUserPosts(),
|
|
],
|
|
));
|
|
});
|
|
}
|
|
|
|
changeView(String viewName) {
|
|
setState(() {
|
|
view = viewName;
|
|
});
|
|
}
|
|
|
|
int _countFollowings(Map followings) {
|
|
int count = 0;
|
|
|
|
void countValues(key, value) {
|
|
if (value) {
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
followings.forEach(countValues);
|
|
|
|
return count;
|
|
}
|
|
|
|
// ensures state is kept when switching pages
|
|
@override
|
|
bool get wantKeepAlive => true;
|
|
}
|
|
|
|
class ImageTile extends StatelessWidget {
|
|
final ImagePost imagePost;
|
|
|
|
ImageTile(this.imagePost);
|
|
|
|
clickedImage(BuildContext context) {
|
|
Navigator.of(context)
|
|
.push(new MaterialPageRoute<bool>(builder: (BuildContext context) {
|
|
return new Center(
|
|
child: new Scaffold(
|
|
appBar: new AppBar(
|
|
title: new Text('Photo',
|
|
style: new TextStyle(
|
|
color: Colors.black, fontWeight: FontWeight.bold)),
|
|
backgroundColor: Colors.white,
|
|
),
|
|
body: new ListView(
|
|
children: <Widget>[
|
|
new Container(
|
|
child: imagePost,
|
|
),
|
|
],
|
|
)),
|
|
);
|
|
}));
|
|
}
|
|
|
|
Widget build(BuildContext context) {
|
|
return new GestureDetector(
|
|
onTap: () => clickedImage(context),
|
|
child: new Image.network(imagePost.mediaUrl, fit: BoxFit.cover));
|
|
}
|
|
}
|
|
|
|
class User {
|
|
const User(
|
|
{this.username,
|
|
this.id,
|
|
this.photoUrl,
|
|
this.email,
|
|
this.displayName,
|
|
this.bio,
|
|
this.followers,
|
|
this.following});
|
|
|
|
final String email;
|
|
final String id;
|
|
final String photoUrl;
|
|
final String username;
|
|
final String displayName;
|
|
final String bio;
|
|
final Map followers;
|
|
final Map following;
|
|
|
|
factory User.fromDocument(DocumentSnapshot document) {
|
|
return new User(
|
|
email: document['email'],
|
|
username: document['username'],
|
|
photoUrl: document['photoUrl'],
|
|
id: document.documentID,
|
|
displayName: document['displayName'],
|
|
bio: document['bio'],
|
|
followers: document['followers'],
|
|
following: document['following'],
|
|
);
|
|
}
|
|
}
|
|
|
|
void openProfile(BuildContext context, String userId) {
|
|
Navigator.of(context)
|
|
.push(new MaterialPageRoute<bool>(builder: (BuildContext context) {
|
|
return new ProfilePage(userId: userId);
|
|
}));
|
|
}
|