Browse Source

feat:1.2版本的

Primroses 4 years ago
parent
commit
500a713d19
36 changed files with 2974 additions and 731 deletions
  1. 4 0
      lib/app.dart
  2. BIN
      lib/assets/img/mine_icon_code.png
  3. BIN
      lib/assets/img/share_icon_friends.png
  4. 233 0
      lib/bean/message.dart
  5. 4 0
      lib/bean/post.dart
  6. 62 5
      lib/bean/user_friend.dart
  7. 101 0
      lib/db/message_db.dart
  8. 4 1
      lib/pages/game/rank_people_detail.dart
  9. 21 21
      lib/pages/home/home_info_page.dart
  10. 7 3
      lib/pages/home_page.dart
  11. 191 162
      lib/pages/my/my_page.dart
  12. 4 3
      lib/pages/my/user_info_page.dart
  13. 63 38
      lib/pages/social/chat_page.dart
  14. 195 141
      lib/pages/social/message_page.dart
  15. 71 44
      lib/pages/social/post_detail_page.dart
  16. 159 4
      lib/pages/social/post_share_page.dart
  17. 3 1
      lib/pages/social/post_widget.dart
  18. 346 96
      lib/pages/social/search_page.dart
  19. 175 18
      lib/pages/social/share_achievement.dart
  20. 2 1
      lib/pages/social/share_webview.dart
  21. 4 1
      lib/pages/social/user_detail_page.dart
  22. 116 28
      lib/pages/social/user_friend_add_page.dart
  23. 30 0
      lib/provider/lib/simple_model.dart
  24. 130 0
      lib/provider/message_model.dart
  25. 1 1
      lib/router/routes.dart
  26. 9 0
      lib/services/Converter.dart
  27. 44 0
      lib/services/api/rest_client.dart
  28. 248 0
      lib/services/api/rest_client.g.dart
  29. 19 5
      lib/sharesdk/tencent.dart
  30. 29 15
      lib/sharesdk/wechat.dart
  31. 51 0
      lib/widgets/button_cancel.dart
  32. 25 6
      lib/widgets/dialog/alert_dialog.dart
  33. 262 0
      lib/widgets/dialog/scan_add_new_friend_dialog.dart
  34. 54 30
      lib/widgets/dialog/share_popup.dart
  35. 79 25
      lib/widgets/menu_bar.dart
  36. 228 82
      lib/widgets/menu_share_bottom.dart

+ 4 - 0
lib/app.dart

@@ -6,6 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:provider/provider.dart';
 import 'package:sport/pages/home_page.dart';
 import 'package:sport/provider/bluetooth.dart';
+import 'package:sport/provider/message_model.dart';
 import 'package:sport/utils/CupertinoLocalizationsDelegate.dart';
 
 import 'application.dart';
@@ -27,6 +28,9 @@ class MyApp extends StatelessWidget {
         ChangeNotifierProvider<Bluetooth>(
           create: (_) => Bluetooth(),
         ),
+        ChangeNotifierProvider<MessageModel>(
+          create: (_) => MessageModel(),
+        ),
       ],
       child: MaterialApp(
         debugShowCheckedModeBanner: false,

BIN
lib/assets/img/mine_icon_code.png


BIN
lib/assets/img/share_icon_friends.png


+ 233 - 0
lib/bean/message.dart

@@ -0,0 +1,233 @@
+import 'package:sport/bean/forum.dart';
+import 'package:sport/bean/post.dart';
+
+class MessageInstance {
+  int fromId;
+  int toId;
+  String type;
+  MessageData data;
+  int read;
+  bool selfSend;
+  String createdAt;
+  MessageUser toUser;
+  MessageUser fromUser;
+
+  MessageInstance(
+      {this.fromId,
+      this.toId,
+      this.type,
+      this.data,
+      this.read,
+      this.selfSend,
+      this.createdAt,
+      this.toUser});
+
+  MessageInstance.fromJson(Map<String, dynamic> json) {
+    fromId = json['from_id'];
+    toId = json['to_id'];
+    type = json['type'];
+    data = json['data'] != null ? new MessageData.fromJson(json['data']) : null;
+    read = json['read'];
+    selfSend = json['self_send'];
+    createdAt = json['created_at'];
+    toUser = json['to_user'] != null
+        ? new MessageUser.fromJson(json['to_user'])
+        : null;
+    fromUser = json['from_user'] != null
+        ? new MessageUser.fromJson(json['from_user'])
+        : null;
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['from_id'] = this.fromId;
+    data['to_id'] = this.toId;
+    data['type'] = this.type;
+    if (this.data != null) {
+      data['data'] = this.data.toJson();
+    }
+    data['read'] = this.read;
+    data['self_send'] = this.selfSend;
+    data['created_at'] = this.createdAt;
+    if (this.toUser != null) {
+      data['to_user'] = this.toUser.toJson();
+    }
+    if (this.fromUser != null) {
+      data['from_user'] = this.toUser.toJson();
+    }
+    return data;
+  }
+}
+
+class MessageData {
+  String text;
+  String url;
+  String logo;
+  MessageUser user;
+  Forum forum;
+  Post subject;
+
+  MessageData(
+      {this.text, this.url, this.logo, this.user, this.forum, this.subject});
+
+  MessageData.fromJson(Map<String, dynamic> json) {
+    text = json['text'];
+    url = json['url'];
+    logo = json['logo'];
+    user = json['user'];
+    forum = json['forum"'];
+    subject = json['subject'];
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> map = new Map<String, dynamic>();
+    map['text'] = this.text;
+    map['url'] = this.url;
+    map['logo'] = this.logo;
+    map['user'] = this.user;
+    map['forum"'] = this.forum;
+    map['subject'] = this.subject;
+    return map;
+  }
+}
+
+class MessageUser {
+  int id;
+  String name;
+  String avatar;
+  bool online;
+
+  MessageUser({this.id, this.name, this.avatar, this.online});
+
+  MessageUser.fromJson(Map<String, dynamic> json) {
+    id = json['id'];
+    name = json['name'];
+    avatar = json['avatar'];
+    online = json['online'];
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['id'] = this.id;
+    data['name'] = this.name;
+    data['avatar'] = this.avatar;
+    data['online'] = this.online;
+    return data;
+  }
+}
+
+// 这里是轮询的消息结构...
+class Message {
+  int curId;
+  List<MessageInstance> messages;
+
+  Message({this.curId, this.messages});
+
+  Message.fromJson(Map<String, dynamic> json) {
+    curId = json['cur_id'];
+    messages = new List<MessageInstance>();
+    if (json['messages'] != null) {
+      json['messages'].forEach((v) {
+        messages.add(new MessageInstance.fromJson(v));
+      });
+    }
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['id'] = this.curId;
+    if (this.messages != null) {
+      data['messages'] = this.messages.map((v) => v.toJson()).toList();
+    }
+
+    return data;
+  }
+}
+
+class ChatMessage {
+  MessageUser user;
+  List<MessageInstance> messages;
+
+  ChatMessage({this.user, this.messages});
+
+  ChatMessage.fromJson(Map<String, dynamic> json) {
+    user = json['user'];
+    messages = new List<MessageInstance>();
+    if (json['message'] != null) {
+      json['message'].forEach((v) {
+        messages.add(new MessageInstance.fromJson(v));
+      });
+    }
+  }
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['user'] = this.user;
+    if (this.messages != null) {
+      data['messages'] = this.messages.map((v) => v.toJson()).toList();
+    }
+    return data;
+  }
+}
+
+// 聊天首页
+class ChatMessageInstance {
+  int id;
+  int fromId;
+  int toId;
+  String type;
+  String relate;
+  bool selfSend;
+  int read;
+  int unreadCount;
+  String createdAt;
+  MessageData data;
+  MessageUser toUser;
+  MessageUser fromUser;
+
+  ChatMessageInstance({
+    this.id,
+    this.fromId,
+    this.toId,
+    this.type,
+    this.relate,
+    this.selfSend,
+    this.read,
+    this.unreadCount,
+    this.createdAt,
+    this.data,
+    this.toUser,
+    this.fromUser,
+  });
+
+  ChatMessageInstance.fromJson(Map<String, dynamic> json) {
+    id = json['id'];
+    fromId = json['from_id'];
+    toId = json['to_id'];
+    type = json['type'];
+    relate = json['relate'];
+    selfSend = json['self_send'];
+    read = json['read'];
+    unreadCount = json['unread_count'];
+    createdAt = json['created_at'];
+    data = json['data'];
+    toUser = json['to_user'];
+    fromUser = json['from_user'];
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['id'] = this.id;
+    data['from_id'] = this.fromId;
+    data['to_id'] = this.toId;
+    data['type'] = this.type;
+    data['relate'] = this.relate;
+    data['self_send'] = this.selfSend;
+    data['read'] = this.read;
+    data['unread_count'] = this.unreadCount;
+    data['created_at'] = this.createdAt;
+    data['data'] = this.data;
+    data['to_user'] = this.toUser;
+    data['from_user'] = this.fromUser;
+    return data;
+  }
+}

+ 4 - 0
lib/bean/post.dart

@@ -137,5 +137,9 @@ class Post {
   int incLikeCount() {
     return max(0, likeCount + (isLiked ? 1 : -1));
   }
+
+  bool isFriend(){
+    return followStatus == "friends" || followStatus == "followed";
+  }
 }
 

+ 62 - 5
lib/bean/user_friend.dart

@@ -12,11 +12,11 @@ class UserFriend {
 
   UserFriend(
       {this.uid,
-        this.isFriends,
-        this.groups,
-        this.createdAt,
-        this.updatedAt,
-        this.socialInfo});
+      this.isFriends,
+      this.groups,
+      this.createdAt,
+      this.updatedAt,
+      this.socialInfo});
 
   UserFriend.fromJson(Map<String, dynamic> json) {
     uid = Converter.toInt(json['uid']);
@@ -43,3 +43,60 @@ class UserFriend {
     return data;
   }
 }
+
+class NewFriend {
+  int id;
+  String name;
+  String level;
+  String score;
+  String isBan;
+  String provinceId;
+  String cityId;
+  String districtId;
+  String gender;
+  String age;
+  String avatar;
+  String city;
+  NewFriend(
+      this.id,
+      this.name,
+      this.level,
+      this.score,
+      this.isBan,
+      this.provinceId,
+      this.cityId,
+      this.districtId,
+      this.gender,
+      this.age,
+      this.avatar,
+      this.city);
+  NewFriend.fromJson(Map<String, dynamic> json) {
+    id = Converter.toInt(json['id']);
+    name = json['name'];
+    level = json['level'];
+    score = json['score'];
+    isBan = json['is_ban'];
+    provinceId = json['province_id'];
+    cityId = json['city_id'];
+    districtId = json['district_id'];
+    gender = json['gender'];
+    age = json['age'];
+    avatar = json['avatar'];
+    city = json['city'];
+  }
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['id'] = this.id;
+    data['name'] = this.name;
+    data['level'] = this.level;
+    data['isBan'] = this.isBan;
+    data['provinceId'] = this.provinceId;
+    data['cityId'] = this.cityId;
+    data['districtId'] = this.districtId;
+    data['gender'] = this.gender;
+    data['age'] = this.age;
+    data['avatar'] = this.avatar;
+    data['city'] = this.city;
+    return data;
+  }
+}

+ 101 - 0
lib/db/message_db.dart

@@ -0,0 +1,101 @@
+import 'dart:async';
+
+import 'package:path/path.dart';
+import 'package:sqflite/sqflite.dart';
+
+class MessageItem {
+  String message;
+  int status;
+  int curId;
+  MessageItem({this.message, this.status, this.curId});
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['message'] = this.message;
+    data['status'] = this.status;
+    data['curId'] = this.curId;
+    return data;
+  }
+}
+
+class MessageDB {
+  static final MessageDB _instance = new MessageDB.internal();
+
+  factory MessageDB() => _instance;
+
+  static Database _db;
+
+  MessageDB.internal();
+
+  Future<Database> get db async {
+    if (_db != null) {
+      return _db;
+    }
+    _db = await initDb();
+
+    return _db;
+  }
+
+  final String TABLE = 'Message';
+
+  initDb() async {
+    String databasesPath = await getDatabasesPath();
+    String path = join(databasesPath, 'message.db');
+
+    var db = await openDatabase(path, version: 1, onCreate: _onCreate);
+    return db;
+  }
+
+  FutureOr<void> _onCreate(Database db, int version) async {
+    await db.execute(
+        'CREATE TABLE $TABLE(messageId INTEGER PRIMARY KEY, status INTEGER ,message TEXT, curId INTEGER)');
+  }
+
+  Future<int> insert(MessageItem item) async {
+    var dbClient = await db;
+    var result = await dbClient.insert(TABLE, item.toJson());
+    return result;
+  }
+
+  Future insertAll(List<MessageItem> items) async {
+    var dbClient = await db;
+    var batch = dbClient.batch();
+    items.forEach((item) {
+      batch.insert(TABLE, item.toJson());
+    });
+    batch.commit();
+  }
+
+  // 最新的那些...
+  Future<List<Map<String, dynamic>>> findLatest() async {
+    var dbClient = await db;
+    return await dbClient.rawQuery(
+      'SELECT * FROM $TABLE ORDER BY curId DESC LIMIT 1',
+    );
+  }
+
+  Future<List<Map<String, dynamic>>> findAll() async {
+    var dbClient = await db;
+    return await dbClient.rawQuery(
+      'SELECT * FROM $TABLE ORDER BY curId DESC',
+    );
+  }
+
+  Future<List<Map<String, dynamic>>> updateStatus(int curId) async {
+    var dbClient = await db;
+    // status = 1 表示 已读状态...
+    return await dbClient.rawQuery(
+      "UPDATE $TABLE SET status = 1 WHERE curId = $curId;",
+    );
+  }
+
+  Future<int> delete() async {
+    var dbClient = await db;
+    return await dbClient.delete("$TABLE");
+  }
+
+//  Future<int> delete(int time) async {
+//    var dbClient = await db;
+//    return await dbClient.delete(TABLE, where: "time <= ?", whereArgs: [time]);
+//  }
+}

+ 4 - 1
lib/pages/game/rank_people_detail.dart

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
 import 'package:sport/bean/rank_game_info.dart';
 import 'package:sport/bean/user.dart';
 import 'package:sport/bean/user_info.dart';
+import 'package:sport/pages/social/chat_page.dart';
 import 'package:sport/pages/social/private_message_page.dart';
 import 'package:sport/router/navigator_util.dart';
 import 'package:sport/services/api/inject_api.dart';
@@ -104,7 +105,9 @@ class _RankPeopleDetailsPageState extends State<RankPeopleDetailsPage> with Inje
                                   width: 120,
                                   height: 35,
                                   callback: () {
-                                    NavigatorUtil.goPage(context, (context) => PrivateMessagePage(user));
+                                    // 先尝试换一下
+                                    NavigatorUtil.goPage(context, (context) => ChatPage(user.name,user.id));
+                                    // NavigatorUtil.goPage(context, (context) => PrivateMessagePage(user));
                                   },
                                   content: '',
                                   child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[

+ 21 - 21
lib/pages/home/home_info_page.dart

@@ -82,27 +82,27 @@ class _PageState extends ViewStateLifecycle<HomeInfoPage, SportIndexModel> with
           onRefresh: () => refresh(),
           header: buildClassicalHeader(bgColor: Color(0xff241D19)),
           slivers: <Widget>[
-            // 暂时测试
-            if (model.isIdle && value.data != null)
-              SliverToBoxAdapter(
-                child: Stack(
-                  fit: StackFit.passthrough,
-                  children: <Widget>[
-                    CustomPaint(
-                      painter: _HeaderBackground(),
-                      child: Container(
-                        height: 116,
-                      ),
-                    ),
-                    Container(
-                      margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0),
-                      decoration: card(),
-                      height: 156,
-                      child: _buildSportWidget(value.data),
-                    )
-                  ],
-                ),
-              ),
+//            // 暂时测试
+//            if (model.isIdle && value.data != null)
+//              SliverToBoxAdapter(
+//                child: Stack(
+//                  fit: StackFit.passthrough,
+//                  children: <Widget>[
+//                    CustomPaint(
+//                      painter: _HeaderBackground(),
+//                      child: Container(
+//                        height: 116,
+//                      ),
+//                    ),
+//                    Container(
+//                      margin: EdgeInsets.fromLTRB(ui_padding, 4.0, ui_padding, 10.0),
+//                      decoration: card(),
+//                      height: 156,
+//                      child: _buildSportWidget(value.data),
+//                    )
+//                  ],
+//                ),
+//              ),
             if (model.isIdle && value.data != null)
               SliverToBoxAdapter(
                 child: _buildTopWidget(value.data),

+ 7 - 3
lib/pages/home_page.dart

@@ -1,4 +1,5 @@
 import 'package:amap_location_fluttify/amap_location_fluttify.dart';
+import 'package:dartin/dartin.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
@@ -9,6 +10,7 @@ import 'package:sport/pages/home/home_info_page.dart';
 import 'package:sport/pages/my/my_page.dart';
 import 'package:sport/pages/social/social_index_page.dart';
 import 'package:sport/provider/bluetooth.dart';
+import 'package:sport/provider/message_model.dart';
 import 'package:sport/services/app_lifecycle_state.dart';
 import 'package:sport/utils/toast.dart';
 import 'package:sport/utils/update_apk.dart';
@@ -32,12 +34,13 @@ class _HomePageState extends LifecycleState<HomePage> with ConfigInject {
 
   final List<Widget> _pages = <Widget>[HomeInfoPage(), GamePage(), SocialIndexPage(), MyPage()];
 
+
   @override
   void initState() {
     super.initState();
-    Provider.of<Bluetooth>(context, listen: false).listen();
+//    Provider.of<Bluetooth>(context, listen: false).listen();
     updateApp(context);
-//    Navigator.push(context,PopRoute(child: showSharePopup()))
+    Provider.of<MessageModel>(context, listen: false).init(); // 这就开始轮询了?
   }
 
   @override
@@ -116,7 +119,8 @@ class _HomePageState extends LifecycleState<HomePage> with ConfigInject {
               onTap: (index) {
                 _pageController.jumpToPage(index);
                 _valueNotifierIndex.value = index;
-                  Navigator.push( context, new PopRoute(child: sharePopup()));
+                // 这里得轮询解决
+//                  Navigator.push( context, new PopRoute(child: sharePopup()));
               },
             ),
           ),

+ 191 - 162
lib/pages/my/my_page.dart

@@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_blue/flutter_blue.dart';
 import 'package:provider/provider.dart';
+import 'package:qr_flutter/qr_flutter.dart';
 import 'package:sport/bean/post_user.dart';
+import 'package:sport/pages/social/user_friend_add_page.dart';
 import 'package:sport/provider/bluetooth.dart';
 import 'package:sport/provider/user_model.dart';
 import 'package:sport/router/navigator_util.dart';
@@ -28,184 +30,211 @@ class _MyPageState extends State<MyPage> with InjectApi {
   @override
   Widget build(BuildContext context) {
     return AnnotatedRegion<SystemUiOverlayStyle>(
-      value: SystemUiOverlayStyle.light,
+      value: SystemUiOverlayStyle.dark,
       child: Material(
         child: Scaffold(
           backgroundColor: Colors.white,
-          body: Column(
-            children: <Widget>[
-              // 放外面一点的吧
-              InkWell(
-                onTap: () => NavigatorUtil.go(context, Routes.userInfo),
-                child: AspectRatio(
-                  aspectRatio: 1125 / 435.0,
-                  child: Container(
-                    alignment: Alignment.bottomCenter,
-                    padding: EdgeInsets.fromLTRB(24.0, 0, 24.0, 34.0),
-                    decoration: new BoxDecoration(
-                      image: new DecorationImage(
-                        image: new AssetImage("lib/assets/img/bg_userprofile.jpg"),
-                      ),
-                    ),
-                    child: Consumer<UserModel>(
-                      builder: (_, model, __) => model.user == null
-                          ? Container()
-                          : Row(
-                              children: <Widget>[
-                                CircleAvatar(
-                                  backgroundImage: userAvatarProvider(model.user.avatar),
-                                  radius: 30,
-                                ),
-                                Space(
-                                  width: 12,
-                                ),
-                                Column(
-                                  mainAxisSize: MainAxisSize.min,
-                                  mainAxisAlignment: MainAxisAlignment.center,
-                                  crossAxisAlignment: CrossAxisAlignment.start,
+          body: SafeArea(
+            child: Column(
+              children: <Widget>[
+                // 放外面一点的吧
+//              InkWell(
+//                onTap: () => NavigatorUtil.go(context, Routes.userInfo),
+//                child:
+//                AspectRatio(
+//                  aspectRatio: 1125 / 435.0,
+//                  child: Container(
+//                    alignment: Alignment.bottomCenter,
+//                    padding: EdgeInsets.fromLTRB(24.0, 0, 24.0, 34.0),
+//                    decoration: new BoxDecoration(
+////                      image: new DecorationImage(
+////                        image: new AssetImage("lib/assets/img/bg_userprofile.jpg"),
+////                      ),
+//                    ),
+//                  ),
+//                ),
+//              ),
+                Space(height: 37.0,),
+
+                Container(
+                    padding: EdgeInsets.only(top: 12.0),
+                    transform: Matrix4.translationValues(0, -10, 0),
+                    decoration: BoxDecoration(borderRadius: BorderRadius.vertical(top: Radius.circular(10)), color: Colors.white),
+                    child: ListView(
+                      padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 0),
+                      physics: NeverScrollableScrollPhysics(),
+                      shrinkWrap: true,
+                      children: divideTiles(
+                        context: context,
+                        includeLast: true,
+                        tiles: [
+                          ListTile(
+                            contentPadding: EdgeInsets.only(bottom: 20, top: 5),
+                            title: Consumer<UserModel>(
+                                builder: (_, model, __) => model.user == null
+                                    ? Container()
+                                    : Row(
+                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                   children: <Widget>[
-                                    Text(model.user.name ?? "", style: TextStyle(color: Colors.white, fontSize: 18)),
-                                    Space(
-                                      height: 7,
-                                    ),
                                     Row(
                                       children: <Widget>[
-                                        Row(
+                                        CircleAvatar(
+                                          backgroundImage: userAvatarProvider(model.user.avatar),
+                                          radius: 30,
+                                        ),
+                                        Space(
+                                          width: 12,
+                                        ),
+                                        Column(
+                                          mainAxisSize: MainAxisSize.min,
+                                          mainAxisAlignment: MainAxisAlignment.center,
+                                          crossAxisAlignment: CrossAxisAlignment.start,
                                           children: <Widget>[
-                                            Image.asset("lib/assets/img/mine_icon_${model.user.gender == 1 ? "manl" : "girl"}_white.png"),
+                                            Text(model.user.name ?? "", style: TextStyle(color: Color(0xff333333), fontSize: 18)),
                                             Space(
-                                              width: 4,
+                                              height: 7,
                                             ),
-                                            Text(
-                                              model.user.gender == 1 ? "男" : "女",
-                                              style: TextStyle(color: Colors.white, fontSize: 12),
+                                            Row(
+                                              children: <Widget>[
+                                                Row(
+                                                  children: <Widget>[
+                                                    Image.asset("lib/assets/img/mine_icon_${model.user.gender == 1 ? "manl" : "girl"}_white.png"),
+                                                    Space(
+                                                      width: 4,
+                                                    ),
+                                                    Text(
+                                                      model.user.gender == 1 ? "男" : "女",
+                                                      style: TextStyle(color: Color(0xff999999), fontSize: 12),
+                                                    ),
+                                                  ],
+                                                ),
+                                                if ((model.user.age ?? 0) != 0)
+                                                  Padding(
+                                                    padding: const EdgeInsets.only(left: 10.0),
+                                                    child: Text(
+                                                      "${model.user.age}岁",
+                                                      style: TextStyle(color: Color(0xff999999), fontSize: 12),
+                                                    ),
+                                                  ),
+                                                if (model.user.city != null)
+                                                  Padding(
+                                                    padding: const EdgeInsets.only(left: 10.0),
+                                                    child: Text(
+                                                      "${model.user.province}${model.user.province == model.user.city ? '' : model.user.city}",
+                                                      style: TextStyle(color: Colors.white, fontSize: 12),
+                                                    ),
+                                                  ),
+                                              ],
                                             ),
                                           ],
                                         ),
-                                        if ((model.user.age ?? 0) != 0)
-                                          Padding(
-                                            padding: const EdgeInsets.only(left: 10.0),
-                                            child: Text(
-                                              "${model.user.age}岁",
-                                              style: TextStyle(color: Colors.white, fontSize: 12),
-                                            ),
-                                          ),
-                                        if (model.user.city != null)
-                                          Padding(
-                                            padding: const EdgeInsets.only(left: 10.0),
-                                            child: Text(
-                                              "${model.user.province}${model.user.province == model.user.city ? '' : model.user.city}",
-                                              style: TextStyle(color: Colors.white, fontSize: 12),
-                                            ),
-                                          ),
+                                      ],
+                                    ),
+                                    Row(
+                                      children: <Widget>[
+                                        Container(
+                                          width: 22.0,
+                                          height: 22.0,
+                                          child: Image.asset("lib/assets/img/mine_icon_code.png"),
+                                        ),
+                                        Space(
+                                          width: 17.0,
+                                        ),
+                                        arrowRight5(),
                                       ],
                                     )
-                                  ],
-                                ),
-                              ],
+                                    ,],
+                                )
                             ),
-                    ),
-                  ),
-                ),
-              ),
-              Container(
-                padding: EdgeInsets.only(top: 12.0),
-                  transform: Matrix4.translationValues(0, -10, 0),
-                  decoration: BoxDecoration(borderRadius: BorderRadius.vertical(top: Radius.circular(10)), color: Colors.white),
-                  child: ListView(
-                    padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 0),
-                    physics: NeverScrollableScrollPhysics(),
-                    shrinkWrap: true,
-                    children: divideTiles(
-                      context: context,
-                      includeLast: true,
-                      tiles: [
-                        ListTile(
-                          leading: Padding(padding: EdgeInsets.only(bottom: 10, top: 5), child: Image.asset("lib/assets/img/mine_image_shoe.png")),
-                          title: Padding(
-                              padding: EdgeInsets.fromLTRB(12.0, 0, 12.0, 10),
-                              child: Selector<Bluetooth, BluetoothDevice>(
-                                selector: (_, bluetooth) => bluetooth.device,
-                                builder: (_, device, ___) {
-                                  return device == null
-                                      ? Container()
-                                      : ValueListenableBuilder(
-                                          valueListenable: Provider.of<Bluetooth>(context, listen: false).electricityNotifier,
-                                          builder: (_, data, ___) => Column(
-                                                crossAxisAlignment: CrossAxisAlignment.start,
-                                                children: <Widget>[
-                                                  Text(
-                                                    "剩余电量 $data%",
-                                                    style: Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 12),
-                                                  ),
-                                                  Space(
-                                                    height: 8,
-                                                  ),
-                                                  ClipRRect(
-                                                    borderRadius: BorderRadius.circular(10),
-                                                    child: Container(
-                                                      child: LinearProgressIndicator(
-                                                        value: data / 100,
-                                                        valueColor: AlwaysStoppedAnimation(Color.fromRGBO(0, 220, 66, 1)
+                            onTap:() => NavigatorUtil.go(context, Routes.userInfo),
+                          ),
+                          ListTile(
+                            leading: Padding(padding: EdgeInsets.only(bottom: 10, top: 5), child: Image.asset("lib/assets/img/mine_image_shoe.png")),
+                            title: Padding(
+                                padding: EdgeInsets.fromLTRB(12.0, 0, 12.0, 10),
+                                child: Selector<Bluetooth, BluetoothDevice>(
+                                  selector: (_, bluetooth) => bluetooth.device,
+                                  builder: (_, device, ___) {
+                                    return device == null
+                                        ? Container()
+                                        : ValueListenableBuilder(
+                                        valueListenable: Provider.of<Bluetooth>(context, listen: false).electricityNotifier,
+                                        builder: (_, data, ___) => Column(
+                                          crossAxisAlignment: CrossAxisAlignment.start,
+                                          children: <Widget>[
+                                            Text(
+                                              "剩余电量 $data%",
+                                              style: Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 12),
+                                            ),
+                                            Space(
+                                              height: 8,
+                                            ),
+                                            ClipRRect(
+                                              borderRadius: BorderRadius.circular(10),
+                                              child: Container(
+                                                child: LinearProgressIndicator(
+                                                  value: data / 100,
+                                                  valueColor: AlwaysStoppedAnimation(Color.fromRGBO(0, 220, 66, 1)
 //                                            Color(0xffFF5B1D)
-                                                            ),
-                                                      ),
-                                                      height: 7,
-                                                    ),
-                                                  )
-                                                ],
-                                              ));
-                                },
-                              )),
-                          trailing: arrowRight5(),
-                          contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
-                          onTap: () {
-                            var device = Provider.of<Bluetooth>(context, listen: false).device;
-                            if (device == null) {
-                              ToastUtil.show("没连接设备!");
-                              return;
-                            }
-                            NavigatorUtil.go(context, Routes.deviceInfo);
-                          },
-                        ),
-                        ListTile(
-                          title: Text("我的等级"),
-                          contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
-                          trailing: arrowRight5(),
-                          onTap: () => NavigatorUtil.go(context, Routes.level),
-                        ),
-                        ListTile(
-                          title: Text("我的运动"),
-                          contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
-                          trailing: arrowRight5(),
-                          onTap: () => NavigatorUtil.go(context, Routes.gamelistpage),
-                        ),
-                        ListTile(
-                          title: Text("我的帖子"),
-                          contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
-                          trailing: arrowRight5(),
-                          onTap: () {
-                            var userModel = Provider.of<UserModel>(context, listen: false);
-                            var user = userModel.user;
-                            NavigatorUtil.goSocialUserDetail(context, PostUser(id: "${user.id}", name: user.name, avatar: user.avatar));
-                          },
-                        ),
-                        ListTile(
-                          title: Text("用户反馈"),
-                          contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
-                          trailing: arrowRight5(),
-                          onTap: () => NavigatorUtil.go(context, Routes.feedback),
-                        ),
-                        ListTile(
-                          title: Text("设置"),
-                          contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
-                          trailing: arrowRight5(),
-                          onTap: () => NavigatorUtil.go(context, Routes.setting),
-                        ),
-                      ],
-                    ).toList(),
-                  )),
-            ],
+                                                  ),
+                                                ),
+                                                height: 7,
+                                              ),
+                                            )
+                                          ],
+                                        ));
+                                  },
+                                )),
+                            trailing: arrowRight5(),
+                            contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
+                            onTap: () {
+                              var device = Provider.of<Bluetooth>(context, listen: false).device;
+                              if (device == null) {
+                                ToastUtil.show("没连接设备!");
+                                return;
+                              }
+                              NavigatorUtil.go(context, Routes.deviceInfo);
+                            },
+                          ),
+                          ListTile(
+                            title: Text("我的等级"),
+                            contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
+                            trailing: arrowRight5(),
+                            onTap: () => NavigatorUtil.go(context, Routes.level),
+                          ),
+                          ListTile(
+                            title: Text("我的运动"),
+                            contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
+                            trailing: arrowRight5(),
+                            onTap: () => NavigatorUtil.go(context, Routes.gamelistpage),
+                          ),
+                          ListTile(
+                            title: Text("我的帖子"),
+                            contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
+                            trailing: arrowRight5(),
+                            onTap: () {
+                              var userModel = Provider.of<UserModel>(context, listen: false);
+                              var user = userModel.user;
+                              NavigatorUtil.goSocialUserDetail(context, PostUser(id: "${user.id}", name: user.name, avatar: user.avatar));
+                            },
+                          ),
+                          ListTile(
+                            title: Text("用户反馈"),
+                            contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
+                            trailing: arrowRight5(),
+                            onTap: () => NavigatorUtil.go(context, Routes.feedback),
+                          ),
+                          ListTile(
+                            title: Text("设置"),
+                            contentPadding: EdgeInsets.symmetric(horizontal: 0.0),
+                            trailing: arrowRight5(),
+                            onTap: () => NavigatorUtil.go(context, Routes.setting),
+                          ),
+                        ],
+                      ).toList(),
+                    )),
+              ],
+            ),
           ),
         ),
       ),

+ 4 - 3
lib/pages/my/user_info_page.dart

@@ -281,9 +281,10 @@ class _PageState extends State<UserInfoPage> with InjectLoginApi {
                     mainAxisAlignment: MainAxisAlignment.spaceBetween,
                     children: <Widget>[
                       Text("二维码名片"),
-                      QrImage(
-                        data:"二维码名片",
-                        size: 37.0,
+                      Container(
+                        width: 22.0,
+                        height: 22.0,
+                        child: Image.asset("lib/assets/img/mine_icon_code.png"),
                       ),
                     ],
                   ),

+ 63 - 38
lib/pages/social/chat_page.dart

@@ -1,10 +1,17 @@
+import 'dart:convert';
 import 'dart:math';
 
 import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:sport/bean/message.dart';
+import 'package:sport/bean/post.dart';
+import 'package:sport/db/message_db.dart';
+import 'package:sport/provider/lib/view_state_model.dart';
 import 'package:sport/services/api/inject_api.dart';
+import 'package:sport/services/api/resp.dart';
 import 'package:sport/utils/toast.dart';
 import 'package:sport/widgets/appbar.dart';
 import 'package:sport/widgets/decoration.dart';
@@ -26,9 +33,29 @@ final List<String> avatarList = [
   "https://wx2.sinaimg.cn/mw1024/a6bdcd78gy1gfnaaubdhhj20m90m9786.jpg",
 ];
 
+// 不封装的话 就直接这样...
+//class ChatPageModel extends ViewStateModel with InjectApi {
+////  final Future _future;
+////  ChatPageModel(this._future);
+//
+//  // 获取聊天信息
+//  Future getUserChat(int userId) async {
+//    ChatMessage message;
+//    try {
+//      message = (await api.getChatUser(userId)).data;
+//    } catch (e) {
+//      print(e);
+//    }
+//    return message;
+//  }
+//}
+
 class ChatPage extends StatefulWidget {
-  final String chatName;
-  ChatPage(this.chatName);
+  final String userName;
+  final int userId;
+  final Post post;
+
+  ChatPage(this.userName, this.userId, {this.post});
 
   @override
   State<StatefulWidget> createState() => _ChatPageState();
@@ -36,53 +63,50 @@ class ChatPage extends StatefulWidget {
 
 class _ChatPageState extends State<ChatPage> with InjectLoginApi, InjectApi {
   GetMenuController _menuController = new GetMenuController();
+//  ChatPageModel _chatPageModel = new ChatPageModel();
+  List<MessageInstance> _messageList = []; // 初始化 为空...
+
+  initState() {
+    super.initState();
+    initMessageList();
+  }
+
+  void initMessageList() async {
+    var list = await MessageDB().findLatest();
+    // 这不是是个骚的?
+    for (var item in list) {
+      _messageList.add(json.decode(item["message"]));
+    }
+    print("$list---------------------------------------");
+  }
 
   @override
   Widget build(BuildContext context) {
-    Widget chatList = CustomScrollView(
-      reverse: true,
-      controller: _menuController.scrollMenuController,
-      slivers: <Widget>[
-        SliverList(
-          delegate: SliverChildBuilderDelegate((content, index) {
-            return Column(
-              children: <Widget>[
-                Padding(
-                  padding: const EdgeInsets.symmetric(
-                      vertical: 12.0, horizontal: 12.0),
-                  child: Text(
-                    "早上9.26",
-                    style: Theme.of(context).textTheme.bodyText1,
-                  ),
-                ),
-                _buildChatItem(context,
-                    type: 1,
-                    msg: "福星再贪一波",
-                    index: index,
-                    imageUrls: avatarList.sublist(6, 11)),
-                _buildChatItem(context,
-                    type: 2,
-                    msg: "我九五之尊,三星瞎子天下无敌",
-                    index: index,
-                    imageUrls: avatarList.sublist(0, 5))
-              ],
-            );
-          }, childCount: 10),
-        )
-      ],
-    );
-
     return Scaffold(
         appBar: AppBar(
           title: Text(
-            "${widget.chatName}",
+            "${widget.userName}",
             style: titleStyle,
           ),
           leading: buildBackButton(context),
         ),
         resizeToAvoidBottomInset: false, // 透传MediaQuery 的高度?
         body: MenuBar(
-          chatList,
+          CustomScrollView(
+            reverse: true,
+            controller: _menuController.scrollMenuController,
+            slivers: <Widget>[
+              SliverList(
+                delegate: SliverChildBuilderDelegate((content, index) {
+                  return Column(
+                    children: <Widget>[],
+                  );
+                }, childCount: 10),
+              )
+            ],
+          ),
+          menuIdentity:
+              new MenuIdentity(menuScene: "chat", userId: widget.userId),
           inputField: "",
           scrollToBottom: _menuController.scrollToBottom,
         ));
@@ -192,7 +216,8 @@ Widget _buildChatItem(BuildContext context,
                       shrinkWrap: true,
                       padding: EdgeInsets.zero,
                       crossAxisSpacing: 10.0,
-                      crossAxisCount: imageUrls.length > 3 ? 3 : imageUrls.length,
+                      crossAxisCount:
+                          imageUrls.length > 3 ? 3 : imageUrls.length,
                       mainAxisSpacing: 10.0,
                       children: imageUrls
                           .asMap()

+ 195 - 141
lib/pages/social/message_page.dart

@@ -1,11 +1,18 @@
+import 'dart:convert';
+
 import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
 import 'package:flutter/material.dart' hide NestedScrollView;
+import 'package:provider/provider.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+import 'package:sport/bean/message.dart';
 import 'package:sport/bean/notice.dart';
 import 'package:sport/bean/post_user.dart';
 import 'package:sport/bean/user_friend.dart';
+import 'package:sport/db/message_db.dart';
 import 'package:sport/pages/social/chat_page.dart';
 import 'package:sport/pages/social/message_notice_detail_page.dart';
 import 'package:sport/pages/social/user_detail_page.dart';
+import 'package:sport/provider/message_model.dart';
 import 'package:sport/router/navigator_util.dart';
 import 'package:sport/router/routes.dart';
 import 'package:sport/services/api/inject_api.dart';
@@ -13,6 +20,7 @@ import 'package:sport/services/api/resp.dart';
 import 'package:sport/utils/DateFormat.dart';
 import 'package:sport/utils/toast.dart';
 import 'package:sport/widgets/appbar.dart';
+import 'package:sport/widgets/error.dart';
 import 'package:sport/widgets/image.dart';
 import 'package:sport/widgets/loading.dart';
 import 'package:sport/widgets/misc.dart';
@@ -27,6 +35,10 @@ class _PageState extends State<MessagePage>
     with TickerProviderStateMixin, InjectApi {
   TabController _controller;
   Future<List<Notice>> _future;
+  Future<List<MessageInstance>> _chatFuture;
+
+  List<MessageInstance> messageList;
+
   final List<String> avatarList = [
     "https://wx3.sinaimg.cn/mw1024/a6bdcd78gy1gfna8yznv5j20m90m9tbz.jpg",
     "https://wx2.sinaimg.cn/mw1024/a6bdcd78gy1gfna8x3nbzj20m90m943a.jpg",
@@ -47,6 +59,7 @@ class _PageState extends State<MessagePage>
     super.initState();
     _controller = TabController(length: 2, vsync: this);
     _future = _getNotice();
+    _chatFuture = getChatIndex();
   }
 
   Future<List<Notice>> _getNotice() async {
@@ -58,6 +71,21 @@ class _PageState extends State<MessagePage>
     return items;
   }
 
+  Future<List<MessageInstance>> getChatIndex() async {
+//    SharedPreferences prefs = await SharedPreferences.getInstance();
+//    String token = prefs.getString("token");
+
+//    List<ChatMessageInstance> list = (await api.getChatIndex()).results;
+    var list = await MessageDB().findAll();
+    // 这不是是个骚的?
+    for (var item in list) {
+//      messageList.add(json.decode(item as String));
+      print("[message:]------${item}-------------------------------");
+    }
+//    print("$list---------------------------------------");
+    return messageList;
+  }
+
   @override
   void dispose() {
     _controller?.dispose();
@@ -322,157 +350,183 @@ class _PageState extends State<MessagePage>
                   const Key('Tab0'),
                   Container(
                     child: FutureBuilder(
-                      future: _future,
-                      builder: (_, AsyncSnapshot<List<Notice>> snapshot) {
-//                        if (snapshot == null || snapshot.data == null) return RequestLoadingWidget();
+                      future: _chatFuture,
+                      builder: (_,
+                          AsyncSnapshot<List<MessageInstance>>
+                              asyncSnapshot) {
+                        if (asyncSnapshot == null || asyncSnapshot.data == null)
+                          return RequestLoadingWidget();
+                        if (asyncSnapshot.data.length == 0)
+                          return Center(
+                            child: RequestErrorWidget(
+                              null,
+                              msg: "暂无消息",
+                              assets: RequestErrorWidget.ASSETS_NO_COMMENT,
+                            ),
+                          );
+                        List<MessageInstance> data = asyncSnapshot.data;
+
+//                        List<ChatMessageInstance> data = [
+//                          new ChatMessageInstance(
+//                              toUser: new MessageUser(name: "别急福星再贪一波",avatar: avatarList[1],id: 10),
+//                              createdAt: "10:26:46",
+//                            data: new MessageData(text:"别急别急")
+//                          ),
+//                          new ChatMessageInstance(
+//                              toUser: new MessageUser(name: "别急福星再贪一波",avatar: avatarList[1],id:11),
+//                              createdAt: "10:26:46",
+//                              data: new MessageData(text:"别急别急")
+//                          ),
+//                        ];
                         return ListView.separated(
-                          padding: EdgeInsets.zero,
-                          itemBuilder: (context, index) {
-//                            if (index >= snapshot.data.length) return Container();
-//                            var item = snapshot.data[index];
-                            return InkWell(
-                              onTap: () async {
-                                await NavigatorUtil.goPage(
-                                    context, (context) => ChatPage("别慌福星再贪一波"));
-                              },
-                              child: Padding(
-                                padding: const EdgeInsets.all(12.0),
-                                child: Column(
-                                  children: <Widget>[
-                                    Padding(
-                                        padding: const EdgeInsets.all(12.0),
-                                        child: Row(
-                                          children: <Widget>[
-                                            index % 2 == 0
-                                                ? ColorFiltered(
-                                                    colorFilter:
-                                                        ColorFilter.mode(
-                                                            Colors.white,
-                                                            BlendMode.color),
-                                                    child: CircleAvatar(
+                            padding: EdgeInsets.zero,
+                            separatorBuilder: (context, index) => Divider(
+                                  height: 1,
+                                ),
+                            itemCount: data?.length ?? 0 + 1,
+                            itemBuilder: (context, index) {
+                              return InkWell(
+                                onTap: () async {
+                                  await NavigatorUtil.goPage(
+                                      context,
+                                      (context) => ChatPage(
+                                          data[index].toUser.name,
+                                          data[index].toUser.id));
+                                },
+                                child: Padding(
+                                  padding: const EdgeInsets.all(12.0),
+                                  child: Column(
+                                    children: <Widget>[
+                                      Padding(
+                                          padding: const EdgeInsets.all(12.0),
+                                          child: Row(
+                                            children: <Widget>[
+                                              index % 2 == 0
+                                                  ? ColorFiltered(
+                                                      colorFilter:
+                                                          ColorFilter.mode(
+                                                              Colors.white,
+                                                              BlendMode.color),
+                                                      child: CircleAvatar(
+                                                        backgroundImage:
+                                                            userAvatarProvider(
+                                                                data[index]
+                                                                    .toUser
+                                                                    .avatar),
+                                                        radius: 22,
+                                                      ),
+                                                    )
+                                                  : CircleAvatar(
                                                       backgroundImage:
                                                           userAvatarProvider(
-                                                              avatarList[
-                                                                  index]),
+                                                              data[index]
+                                                                  .toUser
+                                                                  .avatar),
                                                       radius: 22,
                                                     ),
-                                                  )
-                                                : CircleAvatar(
-                                                    backgroundImage:
-                                                        userAvatarProvider(
-                                                            avatarList[index]),
-                                                    radius: 22,
-                                                  ),
-                                            SizedBox(
-                                              width: 8,
-                                            ),
-                                            Expanded(
-                                              flex: 3,
-                                              child: Column(
-                                                crossAxisAlignment:
-                                                    CrossAxisAlignment.start,
-                                                children: <Widget>[
-                                                  Row(
-                                                    children: <Widget>[
-                                                      Text(
-                                                        "别慌福星再贪一波",
-                                                        style: Theme.of(context)
-                                                            .textTheme
-                                                            .headline3,
-                                                      ),
-                                                      SizedBox(
-                                                        width: 4.0,
-                                                      ),
-                                                      Container(
-                                                          padding: EdgeInsets
-                                                              .symmetric(
-                                                                  horizontal:
-                                                                      5.0),
-                                                          decoration: BoxDecoration(
-                                                              border: Border.all(
-                                                                  color: Color(
-                                                                      0xffffc400)),
-                                                              borderRadius: BorderRadius
-                                                                  .all(Radius
-                                                                      .circular(
-                                                                          8.0))),
-                                                          child: Text(
-                                                            "未关注",
-                                                            style: Theme.of(
-                                                                    context)
-                                                                .textTheme
-                                                                .bodyText1
-                                                                .copyWith(
+                                              SizedBox(
+                                                width: 8,
+                                              ),
+                                              Expanded(
+                                                flex: 3,
+                                                child: Column(
+                                                  crossAxisAlignment:
+                                                      CrossAxisAlignment.start,
+                                                  children: <Widget>[
+                                                    Row(
+                                                      children: <Widget>[
+                                                        Text(
+                                                          "${data[index].toUser.name}",
+                                                          style:
+                                                              Theme.of(context)
+                                                                  .textTheme
+                                                                  .headline3,
+                                                        ),
+                                                        SizedBox(
+                                                          width: 4.0,
+                                                        ),
+                                                        Container(
+                                                            padding: EdgeInsets
+                                                                .symmetric(
+                                                                    horizontal:
+                                                                        5.0),
+                                                            decoration: BoxDecoration(
+                                                                border: Border.all(
                                                                     color: Color(
                                                                         0xffffc400)),
-                                                          ))
-                                                    ],
-                                                  ),
-                                                  SizedBox(
-                                                    height: 4,
-                                                  ),
-                                                  Text(
-                                                    "游戏邀请",
-                                                    style: Theme.of(context)
-                                                        .textTheme
-                                                        .bodyText1,
-                                                  ),
-                                                ],
-                                              ),
-                                            ),
-                                            SizedBox(
-                                              width: 8,
-                                            ),
-                                            Expanded(
-                                              flex: 1,
-                                              child: Column(
-                                                crossAxisAlignment:
-                                                    CrossAxisAlignment.end,
-                                                mainAxisAlignment:
-                                                    MainAxisAlignment.center,
-                                                children: <Widget>[
-                                                  Text(
-                                                    "下午 16:12",
-                                                    style: Theme.of(context)
-                                                        .textTheme
-                                                        .bodyText1,
-                                                  ),
-                                                  SizedBox(
-                                                    height: 3,
-                                                  ),
-                                                  ClipOval(
-                                                      child: Container(
-                                                    width: 21.0,
-                                                    height: 21.0,
-                                                    color: Color(0xffff5B1D),
-                                                    child: Center(
-                                                      child: Text(
-                                                        '1',
-                                                        style: TextStyle(
-                                                            color: Colors.white,
-                                                            fontSize: 12.0),
-                                                      ),
+                                                                borderRadius: BorderRadius
+                                                                    .all(Radius
+                                                                        .circular(
+                                                                            8.0))),
+                                                            child: Text(
+                                                              "未关注",
+                                                              style: Theme.of(
+                                                                      context)
+                                                                  .textTheme
+                                                                  .bodyText1
+                                                                  .copyWith(
+                                                                      color: Color(
+                                                                          0xffffc400)),
+                                                            ))
+                                                      ],
+                                                    ),
+                                                    SizedBox(
+                                                      height: 4,
                                                     ),
-                                                  ))
-                                                ],
+                                                    Text(
+                                                      "游戏邀请",
+                                                      style: Theme.of(context)
+                                                          .textTheme
+                                                          .bodyText1,
+                                                    ),
+                                                  ],
+                                                ),
+                                              ),
+                                              SizedBox(
+                                                width: 8,
                                               ),
-                                            )
-                                          ],
-                                        )),
-                                    Divider(
-                                      height: 1,
-                                    )
-                                  ],
+                                              Expanded(
+                                                flex: 1,
+                                                child: Column(
+                                                  crossAxisAlignment:
+                                                      CrossAxisAlignment.end,
+                                                  mainAxisAlignment:
+                                                      MainAxisAlignment.center,
+                                                  children: <Widget>[
+                                                    Text(
+                                                      "${data[index].createdAt}",
+                                                      style: Theme.of(context)
+                                                          .textTheme
+                                                          .bodyText1,
+                                                    ),
+                                                    SizedBox(
+                                                      height: 3,
+                                                    ),
+                                                    ClipOval(
+                                                        child: Container(
+                                                      width: 21.0,
+                                                      height: 21.0,
+                                                      color: Color(0xffff5B1D),
+                                                      child: Center(
+                                                        child: Text(
+                                                          '1',
+                                                          style: TextStyle(
+                                                              color:
+                                                                  Colors.white,
+                                                              fontSize: 12.0),
+                                                        ),
+                                                      ),
+                                                    ))
+                                                  ],
+                                                ),
+                                              )
+                                            ],
+                                          )),
+                                    ],
+                                  ),
                                 ),
-                              ),
-                            );
-                          },
-                          separatorBuilder: (context, index) => Divider(
-                            height: 1,
-                          ),
-//                          itemCount: snapshot.data?.length ?? 0 + 1,
-                          itemCount: 13,
-                        );
+                              );
+                            });
                       },
                     ),
                   )),

+ 71 - 44
lib/pages/social/post_detail_page.dart

@@ -241,6 +241,7 @@ class _PageState extends ViewStateLifecycle<PostDetailPage, PostDetailModel> wit
 
   Widget _buildPostDetailWidget(Post post) {
     double width = MediaQuery.of(context).size.width - 24;
+
     return Column(
       children: <Widget>[
         Container(
@@ -296,50 +297,76 @@ class _PageState extends ViewStateLifecycle<PostDetailPage, PostDetailModel> wit
                         ),
                       ],
                     ),
-//                    user.isFriends == "1"
-//                        ? Container(
-//                      width: 64,
-//                      height: 30,
-//                      margin: _padding,
-//                      alignment: Alignment.center,
-//                      child: Text(
-//                        "已关注",
-//                        strutStyle: fixedLine,
-//                        style: Theme.of(context).textTheme.bodyText2.copyWith(color: Theme.of(context).accentColor),
-//                      ),
-//                    )
-//                        : GestureDetector(
-//                      child: Container(
-//                        width: 64,
-//                        height: 30,
-//                        margin: _padding,
-//                        alignment: Alignment.center,
-//                        child: Text(
-//                          "关注",
-//                          strutStyle: fixedLine,
-//                          style: Theme.of(context).textTheme.bodyText2.copyWith(color: Theme.of(context).accentColor),
-//                        ),
-//                        decoration: BoxDecoration(
-//                          borderRadius: BorderRadius.circular(20),
-//                          border: Border.all(
-//                            color: Theme.of(context).accentColor,
-//                            width: .5,
-//                          ),
-//                        ),
-//                      ),
-//                      onTap: () async {
-//                        if (user.isFriends == "1") return;
-//                        await request(context, () async {
-//                          var resp = await model.api.userFollow(uid: user?.socialInfo?.id).catchError((onError) {});
-//                          if (resp?.code == 0) {
-//                            ToastUtil.show("关注成功");
-//                            setState(() {
-//                              user.isFriends = "1";
-//                            });
-//                          }
-//                        });
-//                      },
-//                    )
+                    post.isFriend()
+                        ? GestureDetector(
+                      child: Container(
+                        width: 90,
+                        height: 35,
+                        alignment: Alignment.center,
+                        child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
+                          Image.asset("lib/assets/img/mine_icon_followed.png"),
+                          Space(
+                            width: 6,
+                          ),
+                          Text(
+                            "已关注",
+                            strutStyle: fixedLine,
+                            style: Theme.of(context).textTheme.subtitle1.copyWith(color: Theme.of(context).accentColor),
+                          )
+                        ]),
+                        decoration: BoxDecoration(
+                          borderRadius: BorderRadius.circular(20),
+                          border: Border.all(
+                            color: Theme.of(context).accentColor,
+                            width: .5,
+                          ),
+                        ),
+                      ),
+                      onTap: () async {
+                        await request(context, () async {
+                          var resp = await model.api.userUnFollow(uid: int.parse(post.userId)).catchError((onError) {});
+                          if (resp?.code == 0) {
+                            post.followStatus = "none";
+//                            widget.userFriends?.firstWhere((element) => element.uid == user.id)?.isFriends = "0";
+                            setState(() {});
+                            ToastUtil.show("取关成功");
+                          }
+                        });
+                      },
+                    )
+                        : PrimaryButton(
+                      width: 90,
+                      height: 35,
+                      callback: () async {
+                        await request(context, () async {
+                          var resp = await model.api.userFollow(uid: int.parse(post.userId)).catchError((onError) {});
+                          if (resp?.code == 0) {
+                            post.followStatus = "followed";
+                            // 修改本地的不知道为什么这里不要 先保留...
+//                            var db = widget.userFriends?.firstWhere((element) => element.uid == user.id);
+//                            if (db != null) {
+//                              db.isFriends = "1";
+//                              db.isIgnore = 0;
+//                            }
+                            setState(() {});
+                            ToastUtil.show("关注成功");
+                          }
+                        });
+                      },
+                      content: '',
+                      shadow: false,
+                      child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
+                        Image.asset("lib/assets/img/mine_icon_follow.png"),
+                        Space(
+                          width: 6,
+                        ),
+                        Text(
+                          "关注",
+                          strutStyle: fixedLine,
+                          style: Theme.of(context).textTheme.subtitle1.copyWith(color: Colors.white),
+                        )
+                      ]),
+                    ),
                   ],
                 ),
               ),

+ 159 - 4
lib/pages/social/post_share_page.dart

@@ -2,13 +2,18 @@ import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/material.dart';
 import 'package:sport/bean/forum.dart';
 import 'package:sport/bean/post.dart';
+import 'package:sport/bean/user_friend.dart';
+import 'package:sport/pages/social/chat_page.dart';
 import 'package:sport/pages/social/post_page.dart';
 import 'package:sport/router/navigator_util.dart';
 import 'package:sport/services/api/inject_api.dart';
 import 'package:sport/services/api/resp.dart';
+import 'package:sport/utils/toast.dart';
 import 'package:sport/widgets/appbar.dart';
+import 'package:sport/widgets/dialog/alert_dialog.dart';
 import 'package:sport/widgets/image.dart';
 import 'package:sport/widgets/loading.dart';
+import 'package:sport/widgets/space.dart';
 
 class PostSharePage extends StatefulWidget {
   final Post post;
@@ -46,14 +51,21 @@ class _PageState extends State<PostSharePage> with InjectApi {
       body: FutureBuilder<RespList<Forum>>(
         future: api.getForumIndex(),
         builder: (_, snapshot) {
-          if (snapshot.connectionState != ConnectionState.done) return RequestLoadingWidget();
+          if (snapshot.connectionState != ConnectionState.done)
+            return RequestLoadingWidget();
           var list = snapshot.data?.results ?? [];
           return ListView.separated(
             itemBuilder: (BuildContext context, int index) => ListTile(
-              leading: CircleAvatar(backgroundImage: CachedNetworkImageProvider(list[index].cover), radius: 22.0),
+              leading: CircleAvatar(
+                  backgroundImage:
+                      CachedNetworkImageProvider(list[index].cover),
+                  radius: 22.0),
               title: Text(
                 "${list[index].name}",
-                style: Theme.of(context).textTheme.subtitle1.copyWith(fontSize: 16),
+                style: Theme.of(context)
+                    .textTheme
+                    .subtitle1
+                    .copyWith(fontSize: 16),
                 maxLines: 1,
                 overflow: TextOverflow.ellipsis,
               ),
@@ -63,7 +75,7 @@ class _PageState extends State<PostSharePage> with InjectApi {
                     (context) => PostPage(
                           list[index].forumId,
                           post: widget.post,
-                      forum: list[index],
+                          forum: list[index],
                         ));
                 if (result == true) {
                   Navigator.of(context).pop(true);
@@ -79,3 +91,146 @@ class _PageState extends State<PostSharePage> with InjectApi {
     );
   }
 }
+
+class PostShareFriendsPage extends StatefulWidget {
+  final Post post;
+
+  const PostShareFriendsPage(this.post);
+
+  @override
+  State<StatefulWidget> createState() => _PostShareFriendsPageState();
+}
+
+class _PostShareFriendsPageState extends State<PostShareFriendsPage>
+    with InjectApi {
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      backgroundColor: Colors.white,
+      appBar: AppBar(
+        leading: buildBackButton(context),
+        title: Text(
+          "选择分享到的朋友",
+          style: titleStyle,
+        ),
+        centerTitle: false,
+        titleSpacing: 0,
+      ),
+      body: FutureBuilder<RespList<UserFriend>>(
+        future: api.userFriends(),
+        builder: (_, snapshot) {
+          if (snapshot.connectionState != ConnectionState.done)
+            return RequestLoadingWidget();
+          var list = snapshot.data?.results ?? [];
+          return ListView.separated(
+            itemBuilder: (BuildContext context, int index) => ListTile(
+              leading: CircleAvatar(
+                  backgroundImage:
+                      CachedNetworkImageProvider(list[index].socialInfo.avatar),
+                  radius: 22.0),
+              title: Text(
+                "${list[index].socialInfo.name}",
+                style: Theme.of(context)
+                    .textTheme
+                    .subtitle1
+                    .copyWith(fontSize: 16),
+                maxLines: 1,
+                overflow: TextOverflow.ellipsis,
+              ),
+              onTap: () async {
+                await showDialog(
+                    context: context,
+                    builder: (context) => CustomAlertDialog(
+                        child: Column(
+                          children: <Widget>[
+                            Space(
+                              height: 37.0,
+                            ),
+                            CircleAvatar(
+                                backgroundImage: CachedNetworkImageProvider(
+                                  list[index].socialInfo?.avatar,
+                                ),
+                                radius: 35),
+                            Space(
+                              height: 8.0,
+                            ),
+                            Center(
+                              child: Text(
+                                "${list[index].socialInfo?.name}",
+                                style: Theme.of(context).textTheme.headline3,
+                              ),
+                            ),
+                            Space(
+                              height: 8.0,
+                            ),
+                            _buildPostWidget(context, widget.post)
+                          ],
+                        ),
+                        textOk: "分享",
+                        ok: () async {
+                          bool result = await NavigatorUtil.goPage(
+                              context,
+                              (context) => ChatPage(
+                                    list[index].socialInfo.name,
+                                    list[index].socialInfo.id,
+                                    post: widget.post,
+                                  ));
+                          if (result == true) {
+                            Navigator.of(context).pop(true);
+                          }
+                        }));
+              },
+              trailing: arrowRight(),
+            ),
+            itemCount: list.length,
+            separatorBuilder: (BuildContext context, int index) => Divider(),
+          );
+        },
+      ),
+    );
+  }
+}
+
+Widget _buildPostWidget(BuildContext context, Post post) {
+  return Container(
+      margin: const EdgeInsets.symmetric(horizontal: 8.0),
+      padding: const EdgeInsets.symmetric(vertical: 7.0, horizontal: 8.0),
+      decoration: new BoxDecoration(
+        color: Color(0xfff1f1f1), // 底色
+        borderRadius: new BorderRadius.all(Radius.circular(10.0)),
+      ),
+      child: Row(
+        children: <Widget>[
+          if (post.images != null && post.images.isNotEmpty)
+            ClipRRect(
+              child: CachedNetworkImage(
+                imageUrl: post.images[0].src,
+                fit: BoxFit.cover,
+                width: 50,
+                height: 50,
+              ),
+              // 也可控件一边圆角大小
+              borderRadius: new BorderRadius.all(Radius.circular(6.0)),
+            ),
+          Expanded(
+            child: Container(
+              margin: const EdgeInsets.all(5.0),
+              child: Text(post.content,
+                  maxLines: 1,
+                  overflow: TextOverflow.ellipsis,
+                  style: Theme.of(context).textTheme.subtitle1),
+            ),
+          )
+        ],
+      ));
+}

+ 3 - 1
lib/pages/social/post_widget.dart

@@ -19,6 +19,7 @@ import 'package:sport/widgets/dialog/bindphone_dialog.dart';
 import 'package:sport/widgets/dialog/modal_bottom_action.dart';
 import 'package:sport/widgets/dialog/request_dialog.dart';
 import 'package:sport/widgets/image.dart';
+import 'package:sport/widgets/menu_share_bottom.dart';
 import 'package:sport/widgets/misc.dart';
 import 'package:sport/widgets/space.dart';
 
@@ -505,7 +506,8 @@ class _PostWidgetState extends State<PostWidget> with InjectApi {
                       child: GestureDetector(
                         behavior: HitTestBehavior.opaque,
                         onTap: () {
-                          NavigatorUtil.goPage(context, (context) => PostSharePage(post));
+//                          NavigatorUtil.goPage(context, (context) => PostSharePage(post));
+                            menuShareBottom(context,"social",post: post);
                         },
                         child: Container(
                           padding: const EdgeInsets.all(5.0),

+ 346 - 96
lib/pages/social/search_page.dart

@@ -2,19 +2,29 @@ import 'package:flutter/material.dart';
 import 'package:flutter_easyrefresh/easy_refresh.dart';
 import 'package:provider/provider.dart';
 import 'package:sport/bean/post.dart';
+import 'package:sport/bean/post_user.dart';
+import 'package:sport/bean/user_friend.dart';
+import 'package:sport/bean/user_info.dart';
 import 'package:sport/pages/social/post_widget.dart';
+import 'package:sport/pages/social/user_detail_page.dart';
 import 'package:sport/provider/lib/provider_widget.dart' as p;
 import 'package:sport/provider/lib/provider_widget_selector.dart';
+import 'package:sport/provider/lib/simple_model.dart';
+import 'package:sport/provider/lib/view_state_lifecycle.dart';
 import 'package:sport/provider/search_model.dart';
 import 'package:sport/provider/social_detail_model.dart';
 import 'package:sport/router/navigator_util.dart';
+import 'package:sport/services/api/inject_api.dart';
 import 'package:sport/services/api/resp.dart';
 import 'package:sport/services/userid.dart';
+import 'package:sport/utils/toast.dart';
 import 'package:sport/widgets/appbar.dart';
+import 'package:sport/widgets/dialog/request_dialog.dart';
 import 'package:sport/widgets/error.dart';
 import 'package:sport/widgets/image.dart';
 import 'package:sport/widgets/loading.dart';
 import 'package:sport/widgets/misc.dart';
+import 'package:sport/widgets/persistent_header.dart';
 import 'package:sport/widgets/space.dart';
 
 class SearchPage extends StatefulWidget {
@@ -22,13 +32,17 @@ class SearchPage extends StatefulWidget {
   State<StatefulWidget> createState() => _PageState();
 }
 
-class _PageState extends State<SearchPage> with UserId {
+class _PageState extends State<SearchPage> with UserId, InjectApi {
   TextEditingController _controller;
   FocusNode _focusNode;
 
   SearchModel _model;
   SocialDetailModel _searchModel;
 
+  final double tabHeader = 50;
+
+  SimpleModel simpleModel;
+
   @override
   void initState() {
     super.initState();
@@ -38,14 +52,20 @@ class _PageState extends State<SearchPage> with UserId {
       ..getHistory()
       ..getHot();
     _searchModel = SocialDetailModel("0", 100);
+    simpleModel = new SimpleModel((page) async {
+      var list = (await api.userSearch(kw: _model.searchValue, page: page))
+          .pageResult
+          .results;
+      return list;
+    });
   }
 
   @override
   void dispose() {
+    super.dispose();
     _focusNode?.dispose();
     _controller?.dispose();
     _searchModel?.dispose();
-    super.dispose();
   }
 
   _submitValue(String value) {
@@ -70,8 +90,8 @@ class _PageState extends State<SearchPage> with UserId {
             body = _buildDefaultWidget();
             break;
           case SearchBody.suggestions:
-            body = buildSuggestions(context);
-            break;
+//            body = buildSuggestions(context);
+//            break;
           case SearchBody.results:
             body = buildResults(context);
             break;
@@ -97,7 +117,6 @@ class _PageState extends State<SearchPage> with UserId {
                     width: 12,
                   ),
                   Image.asset("lib/assets/img/searchbar_icon_search.png"),
-
                   Expanded(
                       child: Selector<SearchModel, String>(
                     selector: (_, model) => model.searchValue,
@@ -106,13 +125,14 @@ class _PageState extends State<SearchPage> with UserId {
                       maxLines: 1,
                       focusNode: _focusNode,
                       decoration: InputDecoration(
-                        hintText: '请输入搜索内容',
-                          contentPadding: EdgeInsets.symmetric(vertical: 11.0, horizontal: 16.0),
-                        border: InputBorder.none,
-                        hintStyle: TextStyle(color: Color(0xff999999))
-                      ),
+                          hintText: '请输入搜索内容',
+                          contentPadding: EdgeInsets.symmetric(
+                              vertical: 11.0, horizontal: 16.0),
+                          border: InputBorder.none,
+                          hintStyle: TextStyle(color: Color(0xff999999))),
                       onChanged: (value) {
-                        Provider.of<SearchModel>(context, listen: false).updateSearchValue(value);
+                        Provider.of<SearchModel>(context, listen: false)
+                            .updateSearchValue(value);
                       },
                       onSubmitted: (value) {
                         _submitValue(value);
@@ -122,15 +142,20 @@ class _PageState extends State<SearchPage> with UserId {
                     ),
                   )),
                   Visibility(
-                      visible: context.select<SearchModel, String>((value) => value.searchValue).isNotEmpty,
+                      visible: context
+                          .select<SearchModel, String>(
+                              (value) => value.searchValue)
+                          .isNotEmpty,
                       child: GestureDetector(
                         onTap: () {
-                          Provider.of<SearchModel>(context, listen: false).updateSearchValue("");
+                          Provider.of<SearchModel>(context, listen: false)
+                              .updateSearchValue("");
                           _controller.clear();
                         },
                         child: Padding(
                           padding: const EdgeInsets.all(8.0),
-                          child: Image.asset("lib/assets/img/searchbar_btn_no.png"),
+                          child: Image.asset(
+                              "lib/assets/img/searchbar_btn_no.png"),
                         ),
                       ))
                 ],
@@ -164,7 +189,11 @@ class _PageState extends State<SearchPage> with UserId {
                   return Column(
                     crossAxisAlignment: CrossAxisAlignment.start,
                     children: <Widget>[
-                      Text("热门帖子", style: Theme.of(context).textTheme.headline3.copyWith(fontSize: 18)),
+                      Text("热门帖子",
+                          style: Theme.of(context)
+                              .textTheme
+                              .headline3
+                              .copyWith(fontSize: 18)),
                       Padding(
                         padding: const EdgeInsets.symmetric(vertical: 6.0),
                         child: Wrap(
@@ -177,12 +206,15 @@ class _PageState extends State<SearchPage> with UserId {
                                     // _model.queryValue(e);
                                   },
                                   child: Chip(
-                                    labelPadding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0),
+                                    labelPadding: const EdgeInsets.fromLTRB(
+                                        16.0, 0, 16.0, 0),
                                     label: Text("$e"),
-                                    labelStyle: TextStyle(color: Color(0xff666666)),
+                                    labelStyle:
+                                        TextStyle(color: Color(0xff666666)),
                                     backgroundColor: Colors.white,
                                     shape: StadiumBorder(
-                                      side: BorderSide(color: Color(0xffDCDCDC), width: 0.5),
+                                      side: BorderSide(
+                                          color: Color(0xffDCDCDC), width: 0.5),
                                     ),
                                   ));
                             }).toList()),
@@ -208,14 +240,19 @@ class _PageState extends State<SearchPage> with UserId {
                       Row(
                         mainAxisAlignment: MainAxisAlignment.spaceBetween,
                         children: <Widget>[
-                          Text("历史搜索", style: Theme.of(context).textTheme.headline3.copyWith(fontSize: 18)),
+                          Text("历史搜索",
+                              style: Theme.of(context)
+                                  .textTheme
+                                  .headline3
+                                  .copyWith(fontSize: 18)),
                           InkWell(
                             onTap: () {
                               _model.clearHistory();
                             },
                             child: Padding(
                               padding: const EdgeInsets.all(8.0),
-                              child: Image.asset("lib/assets/img/list_icon_del.png"),
+                              child: Image.asset(
+                                  "lib/assets/img/list_icon_del.png"),
                             ),
                           )
                         ],
@@ -233,12 +270,15 @@ class _PageState extends State<SearchPage> with UserId {
                                   // _model.queryValue(e);
                                 },
                                 child: Chip(
-                                  labelPadding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0),
+                                  labelPadding: const EdgeInsets.fromLTRB(
+                                      16.0, 0, 16.0, 0),
                                   label: Text("$e"),
-                                  labelStyle: TextStyle(color: Color(0xff666666)),
+                                  labelStyle:
+                                      TextStyle(color: Color(0xff666666)),
                                   backgroundColor: Colors.white,
                                   shape: StadiumBorder(
-                                    side: BorderSide(color: Color(0xffDCDCDC), width: 0.5),
+                                    side: BorderSide(
+                                        color: Color(0xffDCDCDC), width: 0.5),
                                   ),
                                 ));
                           }).toList(),
@@ -259,91 +299,301 @@ class _PageState extends State<SearchPage> with UserId {
   Widget buildSuggestions(BuildContext context) {
     return FutureBuilder(
       future: _model.api.getPostList(kw: _model.searchValue),
-      builder: (context, AsyncSnapshot<RespPage<Post>> snapshot) => snapshot.connectionState == ConnectionState.done
-          ? snapshot.data.code == 0
-              ? snapshot.data.pageResult.results.isEmpty
-                  ? Center(
-                      child: RequestErrorWidget(
-                      null,
-                      msg: "暂时找不到您想搜索的东西喔",
-                    ))
-                  : ListView.separated(
-                      itemBuilder: (context, index) {
-                        var item = snapshot.data.pageResult.results[index];
-                        return ListTile(
-                          title: Text(
-                            item.content,
-                            maxLines: 1,
-                            overflow: TextOverflow.ellipsis,
-                          ),
-                          onTap: () {
+      builder: (context, AsyncSnapshot<RespPage<Post>> snapshot) =>
+          snapshot.connectionState == ConnectionState.done
+              ? snapshot.data.code == 0
+                  ? snapshot.data.pageResult.results.isEmpty
+                      ? Center(
+                          child: RequestErrorWidget(
+                          null,
+                          msg: "暂时找不到您想搜索的东西喔",
+                        ))
+                      : ListView.separated(
+                          itemBuilder: (context, index) {
+                            var item = snapshot.data.pageResult.results[index];
+                            return ListTile(
+                              title: Text(
+                                item.content,
+                                maxLines: 1,
+                                overflow: TextOverflow.ellipsis,
+                              ),
+                              onTap: () {
 //            query = '老孟 $index';
-                            String value = item.content;
-                            _submitValue(value);
+                                String value = item.content;
+                                _submitValue(value);
 
-                            // NavigatorUtil.goSocialPostDetail(context, item);
+                                // NavigatorUtil.goSocialPostDetail(context, item);
+                              },
+                              trailing: arrowRight(),
+                            );
                           },
-                          trailing: arrowRight(),
-                        );
-                      },
-                      separatorBuilder: (context, index) {
-                        return Divider(
-                          height: 1,
-                          indent: 12,
-                          endIndent: 12,
-                        );
-                      },
-                      itemCount: snapshot.data.pageResult.results.length,
-                    )
-              : Container()
-          : Container(),
+                          separatorBuilder: (context, index) {
+                            return Divider(
+                              height: 1,
+                              indent: 12,
+                              endIndent: 12,
+                            );
+                          },
+                          itemCount: snapshot.data.pageResult.results.length,
+                        )
+                  : Container()
+              : Container(),
     );
   }
 
   Widget buildResults(BuildContext context) {
-    return p.ProviderWidget<SocialDetailModel>(
-      model: _searchModel,
-      autoDispose: false,
-      builder: (_, model, __) {
-        return EasyRefresh.custom(
-          controller: model.refreshController,
-          enableControlFinishRefresh: true,
-          enableControlFinishLoad: true,
-          onLoad: model.isIdle ? () => model.loadMore() : null,
-          header: buildClassicalHeader(),
-          footer: buildClassicalFooter(),
-          slivers: <Widget>[
-            if (model.isBusy)
-              SliverToBoxAdapter(
-                child: RequestLoadingWidget(),
-              ),
-            if (model.isEmpty)
-              SliverToBoxAdapter(
-                child: RequestErrorWidget(
-                  null,
-                  msg: "暂时找不到您想搜索的东西喔",
-                  assets: RequestErrorWidget.ASSETS_NO_MOTION,
+    return DefaultTabController(
+      length: 2,
+      child: Column(
+        children: <Widget>[
+          Container(
+            color: Colors.white,
+            height: tabHeader,
+            padding: EdgeInsets.symmetric(vertical: 8.0),
+            child: TabBar(
+              isScrollable: true,
+              indicatorPadding: EdgeInsets.symmetric(horizontal: 6),
+              indicatorWeight: 3,
+              tabs: <Widget>[
+                Tab(text: "帖子"),
+                Tab(text: "用户"),
+              ],
+            ),
+            alignment: Alignment.centerLeft,
+          ),
+          Expanded(
+            child: TabBarView(
+              children: <Widget>[
+                p.ProviderWidget<SocialDetailModel>(
+                  model: _searchModel,
+                  autoDispose: false, // 自动 dispose
+                  builder: (_, model, __) {
+                    return EasyRefresh.custom(
+                      controller: model.refreshController,
+                      enableControlFinishRefresh: true,
+                      enableControlFinishLoad: true,
+                      onLoad: model.isIdle ? () => model.loadMore() : null,
+                      header: buildClassicalHeader(),
+                      footer: buildClassicalFooter(),
+                      slivers: <Widget>[
+                        if (model.isBusy)
+                          SliverToBoxAdapter(
+                            child: RequestLoadingWidget(),
+                          ),
+                        if (model.isEmpty)
+                          SliverToBoxAdapter(
+                            child: RequestErrorWidget(
+                              null,
+                              msg: "暂时找不到您想搜索的东西喔",
+                              assets: RequestErrorWidget.ASSETS_NO_MOTION,
+                            ),
+                          ),
+                        SliverList(
+                          delegate: SliverChildBuilderDelegate(
+                            (context, index) {
+                              Post post = model.list[index];
+                              return PostWidget(
+                                post,
+                                _searchModel,
+                                selfId == post.userId,
+                                keyword: _controller.text,
+                                highlight: true,
+                              );
+                            },
+                            childCount: model.list.length,
+                          ),
+                        ),
+                      ],
+                    );
+                  },
                 ),
-              ),
-            if (model.isIdle)
-              SliverList(
-                delegate: SliverChildBuilderDelegate(
-                  (context, index) {
-                    Post post = model.list[index];
-                    return PostWidget(
-                      post,
-                      _searchModel,
-                      selfId == post.userId,
-                      keyword: _controller.text,
-                      highlight: true,
+                p.ProviderWidget<SimpleModel>(
+                  model: simpleModel,
+                  onModelReady: (model) => model.initData(),
+                  autoDispose: false,
+                  builder: (_, model, __) {
+                    return EasyRefresh.custom(
+                      firstRefresh: true,
+                      controller: model.refreshController,
+                      enableControlFinishRefresh: true,
+                      enableControlFinishLoad: true,
+                      onLoad: model.isIdle ? () => model.initData() : null,
+                      header: buildClassicalHeader(),
+                      footer: buildClassicalFooter(),
+                      slivers: <Widget>[
+                        if (model.isBusy)
+                          SliverToBoxAdapter(
+                            child: RequestLoadingWidget(),
+                          ),
+                        if (model.isEmpty || model.isError)
+                          SliverToBoxAdapter(
+                            child: RequestErrorWidget(
+                              null,
+                              msg: _model.searchValue == ""
+                                  ? "请输入搜索的关键字1"
+                                  : "暂无相关用户~",
+                            ),
+                          ),
+                        if (model.isIdle)
+                          SliverList(
+                            delegate: SliverChildBuilderDelegate(
+                              (context, index) {
+                                return _buildItem(model.list[index]);
+                              },
+                              childCount: model.list.length,
+                            ),
+                          ),
+                      ],
                     );
                   },
-                  childCount: model.list.length,
                 ),
+              ],
+            ),
+          )
+        ],
+      ),
+    );
+  }
+
+  Widget _buildItem(UserInfo user) {
+    Widget child = Row(
+      children: <Widget>[
+        CircleAvatar(
+          backgroundImage: userAvatarProvider(user?.avatar),
+          radius: 22,
+        ),
+        SizedBox(
+          width: 8,
+        ),
+        Expanded(
+          child: Column(
+            crossAxisAlignment: CrossAxisAlignment.start,
+            children: <Widget>[
+              Text(
+                "${user?.name}",
+                style: Theme.of(context).textTheme.headline3,
               ),
-          ],
-        );
-      },
+              SizedBox(
+                height: 4,
+              ),
+              Text(
+                "ID: ${user?.id}",
+                style: Theme.of(context).textTheme.bodyText2,
+              ),
+            ],
+          ),
+        ),
+        user.isFriend()
+            ? Container(
+                width: 64,
+                height: 30,
+                margin: EdgeInsets.only(left: 8.0),
+                alignment: Alignment.center,
+                child: Text(
+                  "已关注",
+                  strutStyle: fixedLine,
+                  style: Theme.of(context)
+                      .textTheme
+                      .bodyText2
+                      .copyWith(color: Theme.of(context).accentColor),
+                ),
+              )
+            : GestureDetector(
+                child: Container(
+                  width: 64,
+                  height: 30,
+                  margin: EdgeInsets.only(left: 8.0),
+                  alignment: Alignment.center,
+                  child: Text(
+                    "关注",
+                    strutStyle: fixedLine,
+                    style: Theme.of(context)
+                        .textTheme
+                        .bodyText2
+                        .copyWith(color: Theme.of(context).accentColor),
+                  ),
+                  decoration: BoxDecoration(
+                    borderRadius: BorderRadius.circular(20),
+                    border: Border.all(
+                      color: Theme.of(context).accentColor,
+                      width: .5,
+                    ),
+                  ),
+                ),
+                onTap: () async {
+                  if (user.isFriend()) return;
+                  await request(context, () async {
+                    var resp = await simpleModel.api
+                        .userFollow(uid: user?.id)
+                        .catchError((onError) {});
+                    if (resp?.code == 0) {
+                      ToastUtil.show("关注成功");
+                      setState(() {
+                        user.followStatus = "followed";
+                      });
+                    }
+                  });
+                },
+              )
+      ],
+    );
+    return Column(
+      children: <Widget>[
+        Padding(
+          padding: const EdgeInsets.all(12.0),
+          child: InkWell(
+              onTap: () async {
+                List<UserFriend> friends = simpleModel.list
+                    .map((e) => UserFriend(
+                        uid: user.id,
+                        socialInfo: user,
+                        isFriends: user.isFriend() ? "1" : "0"))
+                    .toList();
+                await NavigatorUtil.goPage(
+                    context,
+                    (context) => UserDetailPage(
+                        PostUser(
+                            id: "${user?.id}",
+                            name: user?.name,
+                            avatar: user?.avatar),
+                        userFriends: friends));
+                user.followStatus = friends
+                            .firstWhere((element) => element.uid == user.id)
+                            ?.isFriends ==
+                        "1"
+                    ? "followed"
+                    : "none";
+                setState(() {});
+              },
+              child: child),
+        ),
+        Divider(
+          height: 1,
+        )
+      ],
     );
   }
 }
+
+class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
+  final TabBar child;
+
+  StickyTabBarDelegate({@required this.child});
+
+  @override
+  Widget build(
+      BuildContext context, double shrinkOffset, bool overlapsContent) {
+    return this.child;
+  }
+
+  @override
+  double get maxExtent => this.child.preferredSize.height;
+
+  @override
+  double get minExtent => this.child.preferredSize.height;
+
+  @override
+  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
+    return true;
+  }
+}

+ 175 - 18
lib/pages/social/share_achievement.dart

@@ -1,11 +1,23 @@
+import 'dart:math';
+import 'dart:ui';
+
+import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+
 import 'package:sport/widgets/appbar.dart';
 import 'package:sport/widgets/menu_share_bottom.dart';
+import 'package:sport/widgets/space.dart';
+
+import 'chat_page.dart';
+
+class ShareAchievementPage extends StatelessWidget {
+  final GlobalKey poster = new GlobalKey();
 
-class shareAchievementPage extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
+//    print("${DateTime.now().millisecondsSinceEpoch}");
     // TODO: implement build
     return Scaffold(
         appBar: AppBar(
@@ -13,23 +25,168 @@ class shareAchievementPage extends StatelessWidget {
             "成就分享",
             style: titleStyle,
           ),
-        ),
-        body: Column(
-          children: <Widget>[
-            Expanded(
-              child: Container(color: Color(0xff999999), child: Container(
-
-              )),
-            ),
-            Container(
-              decoration: BoxDecoration(
-                  color: Color(0xffffffff),
-                  borderRadius: BorderRadius.only(
-                      topLeft: Radius.circular(20),
-                      topRight: Radius.circular(20))),
-              child: menuShareBottomContent(context, hasDownload: true),
-            ),
+          actions: <Widget>[
+            IconButton(
+              icon: Image.asset("lib/assets/img/bbs_icon_share.png"),
+              onPressed: () async {
+                await menuShareBottom(context, "Img",
+                    poster: poster, hasDownload: true);
+              },
+            )
           ],
-        ));
+        ),
+        body: Container(
+            color: Color(0xff999999),
+            width: MediaQuery.of(context).size.width * 1,
+            child: Column(
+              children: <Widget>[
+                Expanded(
+                    child: Column(
+                        mainAxisAlignment: MainAxisAlignment.start,
+                        children: <Widget>[
+                      Space(
+                        height: 40.0,
+                      ),
+                      RepaintBoundary(
+                        key: poster,
+                        child: Container(
+                          width: MediaQuery.of(context).size.width * 0.65,
+                          decoration: BoxDecoration(
+                            borderRadius:
+                                BorderRadius.all(Radius.circular(20.0)),
+                            color: Colors.white,
+                          ),
+                          child: Column(
+                            crossAxisAlignment: CrossAxisAlignment.center,
+                            children: <Widget>[
+                              Space(
+                                height: 35.0,
+                              ),
+                              Text(
+                                "恭喜您达到新成就",
+                                style: Theme.of(context).textTheme.headline3,
+                              ),
+                              Space(
+                                height: 5.0,
+                              ),
+                              Text(
+                                "您是第100位获得者",
+                                style: Theme.of(context).textTheme.bodyText1,
+                              ),
+                              Space(
+                                height: 16.0,
+                              ),
+                              CachedNetworkImage(
+                                imageUrl: avatarList[Random().nextInt(10) % 11],
+                                width: 120.0,
+                                height: 120.0,
+                              ),
+                              Space(
+                                height: 8.0,
+                              ),
+                              Text(
+                                "燃脂达人Ⅱ",
+                                style: Theme.of(context).textTheme.headline1,
+                              ),
+                              Space(
+                                height: 16.0,
+                              ),
+                              Text(
+                                "使用智能鞋运动消耗1000kal",
+                                style: Theme.of(context).textTheme.bodyText1,
+                              ),
+                              Space(
+                                height: 5.0,
+                              ),
+                              Container(
+                                height: 25.0,
+                                width: MediaQuery.of(context).size.width * 0.6,
+                                color: Color(0xffFFC400).withOpacity(0.3),
+                                alignment: Alignment.center,
+                                child: Text(
+                                  "经验值+5  积分+1",
+                                  style: Theme.of(context)
+                                      .textTheme
+                                      .subtitle2
+                                      .copyWith(
+                                          color: Theme.of(context).accentColor),
+                                ),
+                              ),
+                              Space(
+                                height: 14.0,
+                              ),
+                              Row(
+                                mainAxisAlignment: MainAxisAlignment.center,
+                                children: <Widget>[
+                                  Expanded(
+                                    child: Divider(
+                                      indent: 50.0,
+                                      endIndent: 10.0,
+                                    ),
+                                  ),
+                                  Text(
+                                    "2020.07.14",
+                                    style:
+                                        Theme.of(context).textTheme.bodyText1,
+                                  ),
+                                  Expanded(
+                                    child: Divider(
+                                      indent: 10.0,
+                                      endIndent: 50.0,
+                                    ),
+                                  ),
+                                ],
+                              ),
+                              Container(
+                                margin: EdgeInsets.only(top: 8.0),
+                                width: MediaQuery.of(context).size.width * 1,
+                                padding: EdgeInsets.fromLTRB(12, 9, 8, 9),
+//                                color: Colors.black,
+                                decoration: BoxDecoration(
+                                    color: Colors.black,
+                                    borderRadius: BorderRadius.only(
+                                        bottomLeft: Radius.circular(20),
+                                        bottomRight: Radius.circular(20))),
+                                child: Row(
+                                  mainAxisAlignment:
+                                      MainAxisAlignment.spaceBetween,
+                                  children: <Widget>[
+                                    Column(
+                                      crossAxisAlignment:
+                                          CrossAxisAlignment.start,
+                                      children: <Widget>[
+                                        Container(
+                                            width: 36,
+                                            height: 36,
+                                            color: Color(0xFFFFC400)),
+                                        Space(
+                                          height: 4.0,
+                                        ),
+                                        Text(
+                                          "这是一段产品sloagn",
+                                          style: Theme.of(context)
+                                              .textTheme
+                                              .bodyText2,
+                                        )
+                                      ],
+                                    ),
+                                    Container(
+                                      width: 60.0,
+                                      height: 60.0,
+                                      child: Center(
+                                        child: Text("二维码"),
+                                      ),
+                                      color: Colors.white,
+                                    ),
+                                  ],
+                                ),
+                              )
+                            ],
+                          ),
+                        ),
+                      )
+                    ])),
+              ],
+            )));
   }
 }

+ 2 - 1
lib/pages/social/share_webview.dart

@@ -73,7 +73,8 @@ class _WebViewSharePageState extends State<WebViewSharePage> {
               child: InkWell(
                 child: Image.asset("lib/assets/img/bbs_icon_share_white.png"),
                 onTap: () {
-                  menuShareBottom(context, url: "http://106.52.104.134:4000/",hasDownload: false);
+                  menuShareBottom(context, "Link",
+                      url: "http://106.52.104.134:4000/", hasDownload: false);
                 },
               ),
 //                color: Colors.red,

+ 4 - 1
lib/pages/social/user_detail_page.dart

@@ -8,6 +8,7 @@ import 'package:sport/bean/post_user.dart';
 import 'package:sport/bean/user_friend.dart';
 import 'package:sport/bean/user_info.dart';
 import 'package:sport/pages/social/block_user_list_page.dart';
+import 'package:sport/pages/social/chat_page.dart';
 import 'package:sport/pages/social/post_widget.dart';
 import 'package:sport/pages/social/private_message_page.dart';
 import 'package:sport/provider/lib/provider_widget.dart';
@@ -242,7 +243,9 @@ class _PageState extends ViewStateLifecycle<UserDetailPage, UserDetailModel> wit
                                                       width: 120,
                                                       height: 35,
                                                       callback: () {
-                                                        NavigatorUtil.goPage(context, (context) => PrivateMessagePage(user));
+                                                        // 先尝试换一下
+                                                        NavigatorUtil.goPage(context, (context) => ChatPage(user.name,user.id));
+                                                        // NavigatorUtil.goPage(context, (context) => PrivateMessagePage(user));
                                                       },
                                                       content: '',
                                                       shadow: false,

+ 116 - 28
lib/pages/social/user_friend_add_page.dart

@@ -1,5 +1,8 @@
+import 'dart:async';
 import 'dart:convert';
 
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart' hide NestedScrollView;
 import 'package:flutter_dong_scan/scan.dart';
 import 'package:flutter_easyrefresh/easy_refresh.dart';
@@ -19,12 +22,15 @@ import 'package:sport/utils/DateFormat.dart';
 import 'package:sport/utils/click.dart';
 import 'package:sport/utils/toast.dart';
 import 'package:sport/widgets/appbar.dart';
+import 'package:sport/widgets/button_cancel.dart';
+import 'package:sport/widgets/dialog/scan_add_new_friend_dialog.dart';
 import 'package:sport/widgets/dialog/request_dialog.dart';
 import 'package:sport/widgets/error.dart';
 import 'package:sport/widgets/image.dart';
 import 'package:sport/widgets/loading.dart';
 import 'package:sport/widgets/misc.dart';
 import 'package:sport/widgets/persistent_header.dart';
+import 'package:sport/widgets/space.dart';
 
 class UserFriendAddPage extends StatefulWidget {
   @override
@@ -37,6 +43,16 @@ class _PageState extends ViewStateLifecycle<UserFriendAddPage, SimpleModel>
   FocusNode _focusNode;
   ValueNotifier<String> _searchValue = ValueNotifier<String>("");
 
+  var userCode; // 用户自己的二维码
+  UserInfo _newFriendUserInfo; // 扫码出来的那个逼的userInfo(请求回来的)
+  //  int dialogType;                 // 弹窗类型
+  String newFriendCode; // 扫码出来的要加的那个逼的二维码
+  List<UserFriend> allUserFriends = []; // 等待添加的好友队列
+  bool isWaiting = false; // 上锁解决
+  Timer timerIsFriend; // 轮询是否是好友
+  Timer timerAllFriend; // 轮询是否有好友加你
+  int currentIndex = 0; // 不要用栈的方法 用指针的方法...
+
   @override
   SimpleModel createModel() => SimpleModel((page) async {
         if (_searchValue.value == "") return [];
@@ -45,11 +61,18 @@ class _PageState extends ViewStateLifecycle<UserFriendAddPage, SimpleModel>
             .results;
       });
 
+//  // 试试 写写 ViewModel
+//  SimpleDataModel friendModel() => SimpleDataModel<NewFriend>((code) async {
+//        return (await api.getNewFriend(code)).data;
+//      });
+
   @override
   void initState() {
     super.initState();
     _focusNode = FocusNode();
     _controller = new TextEditingController(text: '');
+    initFriendCode();
+    pollGetFriendRequest();
   }
 
   @override
@@ -57,6 +80,68 @@ class _PageState extends ViewStateLifecycle<UserFriendAddPage, SimpleModel>
     super.dispose();
     _focusNode?.dispose();
     _controller?.dispose();
+    timerIsFriend?.cancel();
+    timerAllFriend?.cancel();
+  }
+
+  // 初始化 好友码...
+  initFriendCode() async {
+    var code = await api.getNewFriendCode();
+    setState(() {
+      userCode = jsonEncode(code.data);
+    });
+  }
+
+  void _showConfirmDialog() async {
+    await showDialog(
+        context: context,
+        barrierDismissible: false,
+        builder: (context) => ScanAddFriendCustomAlertDialog(
+              userInfo: _newFriendUserInfo,
+              newFriendCode: newFriendCode,
+            ));
+  }
+
+  void pollGetFriendRequest() async {
+    // 获取 userFriends
+    timerIsFriend = Timer.periodic(Duration(seconds: 5), (timer) async {
+      List<UserFriend> userFriends = (await api.getFriendRequest()).results;
+      if (userFriends.length > 0) {
+        if (allUserFriends.length <= 0) {
+          allUserFriends.addAll(userFriends);
+        } else {
+          Iterable<int> allUserFriendsId =
+              allUserFriends.map((e) => e.socialInfo.id);
+          // 去重...
+          for (UserFriend _userfriend in userFriends) {
+            if (!allUserFriendsId.contains(_userfriend.socialInfo.id)) {
+              allUserFriends.add(_userfriend);
+            }
+          }
+        }
+      }
+    });
+    // 暂时这样解决
+    timerAllFriend = Timer.periodic(Duration(seconds: 2), (timer) async {
+      // 终极解决方案... 上锁 + 指针 / 用栈的话 还是会 有多次的。用指针就不会了
+      if (!isWaiting &&
+          allUserFriends.length > 0 &&
+          currentIndex < allUserFriends.length) {
+        isWaiting = true; //正在等待的意思...
+        UserFriend currentUserFriend = allUserFriends[currentIndex];
+        bool flag = await showDialog(
+            context: context,
+            barrierDismissible: false,
+            builder: (context) => ScanAddFriendCustomAlertDialog(
+                  userInfo: currentUserFriend.socialInfo,
+                  isWaitAdd: true,
+                ));
+        if (flag != null) {
+          isWaiting = false;
+          currentIndex += 1;
+        }
+      }
+    });
   }
 
   @override
@@ -75,15 +160,19 @@ class _PageState extends ViewStateLifecycle<UserFriendAddPage, SimpleModel>
             icon: Image.asset("lib/assets/img/fiends_image_scanning.png"),
             onPressed: () {
               ScanConfig scanConfig = ScanConfig();
-              SDScan scan = SDScan().setScanEventListener((dynamic codeString) {
+              SDScan scan =
+                  SDScan().setScanEventListener((dynamic friendData) async {
                 // 扫描之后就去这个逼的 主页 爱关注 不关注的...
-                var data = json.decode(codeString);
-                NavigatorUtil.goSocialUserDetail(
-                    context,
-                    PostUser(
-                        id: data['id'],
-                        name: data['nickname'],
-                        avatar: data['avatar']));
+                var data = json.decode(friendData);
+
+                UserInfo _userInfo =
+                    (await api.getUserInfo('${data['uid']}')).data;
+
+                setState(() {
+                  _newFriendUserInfo = _userInfo;
+                  newFriendCode = data['code'];
+                  _showConfirmDialog();
+                });
               });
               scan.startScan(config: scanConfig);
             },
@@ -133,30 +222,29 @@ class _PageState extends ViewStateLifecycle<UserFriendAddPage, SimpleModel>
                       childCount: model.list.length,
                     ),
                   ),
-                if (!model.isIdle)
+                if (!model.isIdle && userCode != null)
                   SliverToBoxAdapter(
                       child: Padding(
                     padding: EdgeInsets.only(top: 20.0),
                     child: Consumer<UserModel>(
-                      builder: (_, model, __) => model.user == null
-                          ? Center(
-                              child: QrImage(
-                              data:
-                                  '{"id":"${model.user.id}","nickname":"${model.user.name}","avatar":"${model.user.avatar}"}',
-                              version: QrVersions.auto,
-                              size: 170,
-                              gapless: false,
-                            ))
-                          : Center(
-                              child: QrImage(
-                                  data:
-                                      '{"id":"${model.user.id}","nickname":"${model.user.name}","avatar":"${model.user.avatar}"}',
-                                  version: QrVersions.auto,
-                                  size: 170,
-                                  gapless: false,
-                                  embeddedImage:
-                                      userAvatarProvider(model.user.avatar))),
-                    ),
+                        builder: (_, model, __) => model.user == null
+                            ? Center(
+                                child: QrImage(
+                                data: userCode,
+                                version: QrVersions.auto,
+                                size: 170,
+                                gapless: false,
+                              ))
+                            : InkWell(
+                                child: Center(
+                                    child: QrImage(
+                                        data: userCode,
+                                        version: QrVersions.auto,
+                                        size: 170,
+                                        gapless: false,
+                                        embeddedImage: userAvatarProvider(
+                                            model.user.avatar))),
+                              )),
                   )),
               ],
             );

+ 30 - 0
lib/provider/lib/simple_model.dart

@@ -1,5 +1,9 @@
+import 'package:flutter/scheduler.dart';
+import 'package:sport/provider/lib/view_state.dart';
+import 'package:sport/provider/lib/view_state_model.dart';
 import 'package:sport/provider/lib/view_state_refresh_list_model.dart';
 import 'package:sport/services/api/inject_api.dart';
+import 'package:sport/services/api/resp.dart';
 
 typedef FutureCreate = Future<List> Function(int pageNum);
 
@@ -19,3 +23,29 @@ class SimpleModel extends ViewStateRefreshListModel with InjectApi {
     return result;
   }
 }
+
+typedef FutureData = Future Function(String code);
+
+class SimpleDataModel<T> extends ViewStateModel with InjectApi {
+  final FutureData create;
+  RespData data;
+  SimpleDataModel(this.create);
+
+  /// 第一次进入页面loading skeleton
+//  initData() {
+//    SchedulerBinding.instance.addPostFrameCallback((Duration timestamp) {
+//      setBusy();
+////      refresh(init: true); // 不需要refresh
+//    });
+//  }
+
+  Future<T> loadData(String code) async {
+    T result;
+    try {
+      result = await create.call(code);
+    } catch (e) {
+      print(e);
+    }
+    return result;
+  }
+}

+ 130 - 0
lib/provider/message_model.dart

@@ -0,0 +1,130 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/cupertino.dart';
+import 'package:sport/bean/message.dart';
+import 'package:sport/db/message_db.dart';
+import 'package:sport/services/api/inject_api.dart';
+
+class MessageModel extends ChangeNotifier with InjectApi {
+//  Message _message;
+  int _curId;
+  List<MessageInstance> _messages;
+
+  final StreamController<Message> _queryController =
+      StreamController.broadcast();
+
+  Stream<Message> get queryStream => _queryController.stream;
+
+  ///事件订阅对象
+  StreamSubscription _dataSubscription;
+  StreamSubscription periodicSubscription;
+
+  init() {
+    // 监听事件
+    _dataSubscription = queryStream.listen((value) {
+      ///do change
+    });
+    periodicSubscription =
+        Stream.periodic(Duration(seconds: 10)).listen((event) async {
+      Message data;
+      List<MessageItem> _items = [];
+      if (_curId != null) {
+        data = (await api.getMessageForPoll(curId: _curId)).data;
+      } else {
+        data = (await api.getMessageForPoll()).data;
+      }
+      _curId = data.curId;
+      _messages = data.messages;
+
+      // 插入的时候 status 都是 0;
+      if (_messages.length > 0) {
+        var list = await MessageDB().findLatest();
+        int lastCurId = list[0]["curId"];
+        // 可能会重复插入...;
+        if (lastCurId != _curId) {
+          for (MessageInstance _instance in _messages) {
+            _items.add(new MessageItem(
+                message: json.encode(_instance), curId: _curId, status: 0));
+          }
+          await MessageDB().insertAll(_items);
+          print(
+              "[items]:$_items----------------------------------------------");
+        }
+      }
+    });
+  }
+
+  get(Message event) {
+    _queryController.add(event);
+  }
+
+  close() {
+    ///关闭
+    _dataSubscription.cancel();
+    _queryController.close();
+  }
+}
+
+// Generic Interface for all BLoCs
+abstract class BlocBase {
+  void dispose();
+}
+
+// Generic BLoC provider
+class BlocProvider<T extends BlocBase> extends StatefulWidget {
+  BlocProvider({
+    Key key,
+    @required this.child,
+    @required this.bloc,
+  }) : super(key: key);
+
+  final T bloc;
+  final Widget child;
+
+  @override
+  _BlocProviderState<T> createState() => _BlocProviderState<T>();
+
+  static T of<T extends BlocBase>(BuildContext context) {
+    final type = _typeOf<BlocProvider<T>>();
+    BlocProvider<T> provider = context.ancestorWidgetOfExactType(type);
+    return provider.bloc;
+  }
+
+  static Type _typeOf<T>() => T;
+}
+
+class _BlocProviderState<T> extends State<BlocProvider<BlocBase>> {
+  @override
+  void dispose() {
+    widget.bloc.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return widget.child;
+  }
+}
+
+class MessageBloc implements BlocBase {
+  Message _message;
+
+  //
+  // Stream to handle the counter
+  //
+  StreamController<Message> _counterController = StreamController<Message>();
+
+  // 流的输入...
+  StreamSink<Message> get _addOperate => _counterController.sink;
+  Stream<Message> get outCounter => _counterController.stream;
+
+  void dispose() {
+    _counterController.close();
+  }
+
+  // 这是操作?
+  void addOperate(data) {
+    _addOperate.add(_message);
+  }
+}

+ 1 - 1
lib/router/routes.dart

@@ -104,7 +104,7 @@ class Routes {
     }));
 
     router.define(chat, handler: Handler(handlerFunc: (BuildContext context, Map<String, List<Object>> params) {
-      return ChatPage(params['data'].first);
+      return ChatPage(params['userName'].first,params['userId'].first,post: params['post'].first,);
     }));
 
     // home

+ 9 - 0
lib/services/Converter.dart

@@ -4,6 +4,7 @@ import 'package:sport/bean/comment_post.dart';
 import 'package:sport/bean/forum.dart';
 import 'package:sport/bean/game.dart';
 import 'package:sport/bean/image.dart';
+import 'package:sport/bean/message.dart';
 import 'package:sport/bean/notice.dart';
 import 'package:sport/bean/post.dart';
 import 'package:sport/bean/post_user.dart';
@@ -64,6 +65,14 @@ class Converter {
         return UpdateInfo.fromJson(json) as T;
       case "SportStep":
         return SportStep.fromJson(json) as T;
+      case "Message":
+        return Message.fromJson(json) as T;
+      case "NewFriend":
+        return NewFriend.fromJson(json) as T;
+      case "MessageInstance":
+        return MessageInstance.fromJson(json) as T;
+      case "ChatMessageInstance":
+        return ChatMessageInstance.fromJson(json) as T;
     }
     return json as T;
   }

+ 44 - 0
lib/services/api/rest_client.dart

@@ -21,6 +21,7 @@ import 'package:sport/bean/sport_target_today.dart';
 import 'package:sport/bean/user_friend.dart';
 import 'package:sport/bean/user_info.dart';
 import 'package:sport/services/api/resp.dart';
+import 'package:sport/bean/message.dart';
 
 part 'rest_client.g.dart';
 
@@ -207,6 +208,49 @@ abstract class RestClient {
   @GET("/user/search")
   Future<RespPage<UserInfo>> userSearch({@Query("kw") String kw, @Query("p") int page, @Query("limit") int limit = 20});
 
+  @POST("/user/newFriendCode")
+  Future<RespData> getNewFriendCode({@Query("forever") int forever, @Query("forceUpate") int forceUpdate,});
+
+
+  @POST("/user/newFriend")
+  Future<RespData<NewFriend>> getNewFriend(@Query("code") String code);
+
+  @POST("/share/forwardSubject")
+  Future<RespData<NewFriend>> shareForwardSubject(@Query("subject_id") int subjectId,@Query("user_id") int userId,);
+
+
+  @POST("/message/receive")
+  Future<RespData<Message>> getMessageForPoll({@Query("cur_id") int curId});
+
+  @POST("/user/FetchFriendsRequest")
+  Future<RespList<UserFriend>> getFriendRequest();
+
+  @POST("/user/RejectFriend")
+  Future<RespData<bool>> postRejectFriend(@Query("uid") int uid);
+
+  @POST("/user/AcceptFriend")
+  Future<RespData> postAcceptFriend(@Query("uid") int uid);
+
+  @POST("/user/isFriend")
+  Future<RespData<bool>> postIsFriend(@Query("uid") int uid);
+
+  // ********************** 聊天相关 *****************************
+
+  @POST("/chat/index")
+  Future<RespList<ChatMessageInstance>> getChatIndex();
+
+  @POST("/chat/user")
+  Future<RespData<ChatMessage>> getChatUser(@Query("user_id") int userId,{@Query("begin_id") int beginId});
+
+  @POST("/chat/send")
+  Future<RespData<MessageInstance>> postChatSend(@Query("user_id") int userId,@Query("type") String type,@Query("data") String data,);
+
+  @POST("/chat/upload")
+  @Headers(<String, dynamic>{
+    "content-type": "multipart/form-data",
+  })
+  Future<RespData<String>> postChatUpload(@Part(name: "media") File file,);
+
   // ********************** 公告相关 *****************************
   @GET("/inform/list")
   Future<RespPage<Notice>> getInformList({@Query("p") int page});

+ 248 - 0
lib/services/api/rest_client.g.dart

@@ -1156,6 +1156,254 @@ class _RestClient implements RestClient {
   }
 
   @override
+  getNewFriendCode({forever, forceUpdate}) async {
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{
+      r'forever': forever,
+      r'forceUpate': forceUpdate
+    };
+    queryParameters.removeWhere((k, v) => v == null);
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/user/newFriendCode',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<dynamic>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  getNewFriend(code) async {
+    ArgumentError.checkNotNull(code, 'code');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{r'code': code};
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/user/newFriend',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<NewFriend>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  shareForwardSubject(subjectId, userId) async {
+    ArgumentError.checkNotNull(subjectId, 'subjectId');
+    ArgumentError.checkNotNull(userId, 'userId');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{
+      r'subject_id': subjectId,
+      r'user_id': userId
+    };
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/share/forwardSubject',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<NewFriend>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  getMessageForPoll({curId}) async {
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{r'cur_id': curId};
+    queryParameters.removeWhere((k, v) => v == null);
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/message/receive',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<Message>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  getFriendRequest() async {
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/user/FetchFriendsRequest',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespList<UserFriend>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  postRejectFriend(uid) async {
+    ArgumentError.checkNotNull(uid, 'uid');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{r'uid': uid};
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/user/RejectFriend',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<bool>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  postAcceptFriend(uid) async {
+    ArgumentError.checkNotNull(uid, 'uid');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{r'uid': uid};
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/user/AcceptFriend',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<dynamic>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  postIsFriend(uid) async {
+    ArgumentError.checkNotNull(uid, 'uid');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{r'uid': uid};
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/user/isFriend',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<bool>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  getChatIndex() async {
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/chat/index',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespList<ChatMessageInstance>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  getChatUser(userId, {beginId}) async {
+    ArgumentError.checkNotNull(userId, 'userId');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{
+      r'user_id': userId,
+      r'begin_id': beginId
+    };
+    queryParameters.removeWhere((k, v) => v == null);
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/chat/user',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<ChatMessage>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  postChatSend(userId, type, data) async {
+    ArgumentError.checkNotNull(userId, 'userId');
+    ArgumentError.checkNotNull(type, 'type');
+    ArgumentError.checkNotNull(data, 'data');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{
+      r'user_id': userId,
+      r'type': type,
+      r'data': data
+    };
+    final _data = <String, dynamic>{};
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/chat/send',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{},
+            extra: _extra,
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<MessageInstance>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
+  postChatUpload(file) async {
+    ArgumentError.checkNotNull(file, 'file');
+    const _extra = <String, dynamic>{};
+    final queryParameters = <String, dynamic>{};
+    final _data = FormData();
+    _data.files.add(MapEntry(
+        'media',
+        MultipartFile.fromFileSync(file.path,
+            filename: file.path.split(Platform.pathSeparator).last)));
+    final Response<Map<String, dynamic>> _result = await _dio.request(
+        '/chat/upload',
+        queryParameters: queryParameters,
+        options: RequestOptions(
+            method: 'POST',
+            headers: <String, dynamic>{r'content-type': 'multipart/form-data'},
+            extra: _extra,
+            contentType: 'multipart/form-data',
+            baseUrl: baseUrl),
+        data: _data);
+    final value = RespData<String>.fromJson(_result.data);
+    return value;
+  }
+
+  @override
   getInformList({page}) async {
     const _extra = <String, dynamic>{};
     final queryParameters = <String, dynamic>{r'p': page};

+ 19 - 5
lib/sharesdk/tencent.dart

@@ -4,7 +4,8 @@ import 'package:flutter/material.dart';
 import 'package:sport/utils/toast.dart';
 import 'package:tencent_kit/tencent_kit.dart';
 
-typedef QqLogin = void Function(TencentLoginResp login, TencentUserInfoResp userInfo);
+typedef QqLogin = void Function(
+    TencentLoginResp login, TencentUserInfoResp userInfo);
 
 mixin TencentMixin<T extends StatefulWidget> on State<T> {
   static const String _TENCENT_APPID = '1110701531';
@@ -48,25 +49,38 @@ mixin TencentMixin<T extends StatefulWidget> on State<T> {
     );
   }
 
+  void qqShare(String filePath) async {
+    await _tencent.shareImage(
+      scene: TencentScene.SCENE_QQ,
+      imageUri: Uri.file(filePath),
+    );
+  }
+
   void _listenLogin(TencentLoginResp resp) async {
     _loginResp = resp;
-    if (_loginResp != null && _loginResp.isSuccessful() && !_loginResp.isExpired()) {
+    if (_loginResp != null &&
+        _loginResp.isSuccessful() &&
+        !_loginResp.isExpired()) {
       TencentUserInfoResp userInfo = await _tencent.getUserInfo(
         appId: _TENCENT_APPID,
         openid: _loginResp.openid,
         accessToken: _loginResp.accessToken,
       );
       if (userInfo.isSuccessful()) {
-        print('用户信息 ${userInfo.nickname} - ${userInfo.gender} - ${userInfo.genderType} ${userInfo.figureurl1}');
+        print(
+            '用户信息 ${userInfo.nickname} - ${userInfo.gender} - ${userInfo.genderType} ${userInfo.figureurl1}');
       } else {
         ToastUtil.show("获取用户信息:${userInfo.ret} - ${userInfo.msg}");
       }
 
       _qqloginRespStream.add(userInfo);
-    }else{
+    } else {
       _qqloginRespStream.add(null);
     }
   }
 
-  void _listenShare(TencentShareResp resp) {}
+  void _listenShare(TencentShareResp resp) {
+    String content = 'share: ${resp.ret} - ${resp.msg}';
+    print('分享$content}');
+  }
 }

+ 29 - 15
lib/sharesdk/wechat.dart

@@ -46,12 +46,14 @@ mixin WechatMixin<T extends StatefulWidget> on State<T> {
         scope: <String>[WechatScope.SNSAPI_USERINFO],
         state: 'auth',
       );
-      print("wechatLoginwechatLoginwechatLoginwechatLoginwechatLoginwechatLoginwechatLoginwechatLogin");
-      if(_auth == null) {
+      print(
+          "wechatLoginwechatLoginwechatLoginwechatLoginwechatLoginwechatLoginwechatLoginwechatLogin");
+      if (_auth == null) {
         _auth = _wechat.authResp().listen((WechatAuthResp resp) {
           _authResp = resp;
           String content = 'auth: ${resp.errorCode} ${resp.errorMsg}';
-          if (_authResp != null && _authResp.errorCode == WechatSdkResp.ERRORCODE_SUCCESS) {
+          if (_authResp != null &&
+              _authResp.errorCode == WechatSdkResp.ERRORCODE_SUCCESS) {
             weChatLogin.call(_authResp.code);
           } else {
             weChatLogin.call(null);
@@ -70,18 +72,30 @@ mixin WechatMixin<T extends StatefulWidget> on State<T> {
     );
   }
 
-  void wechatShareImage() async {
-    Dio dio = new Dio();
-    Directory documentsDir = await getApplicationDocumentsDirectory();
-    String documentsPath = documentsDir.path;
-    String path = "$documentsPath/demo/game.jpg";
-    await dio.download("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=229276664,935794386&fm=26&gp=0.jpg", "$path",
-        options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}), // disable gzip
-        onReceiveProgress: (received, total) {
-      if (total != -1) {}
-    });
-    print(path);
-    await _wechat.shareImage(scene: WechatScene.SESSION, imageData: File(path).readAsBytesSync());
+//  void wechatShareImage() async {
+//    Dio dio = new Dio();
+//    Directory documentsDir = await getApplicationDocumentsDirectory();
+//    String documentsPath = documentsDir.path;
+//    String path = "$documentsPath/demo/game.jpg";
+//    await dio.download("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=229276664,935794386&fm=26&gp=0.jpg", "$path",
+//        options: Options(headers: {HttpHeaders.acceptEncodingHeader: "*"}), // disable gzip
+//        onReceiveProgress: (received, total) {
+//      if (total != -1) {}
+//    });
+//    print(path);
+//    await _wechat.shareImage(scene: WechatScene.SESSION, imageData: File(path).readAsBytesSync());
+//  }
+
+  void wechatShareImage(String path,String type) async {
+    if(type == "chat"){
+      // 这是 聊天
+      await _wechat.shareImage(
+          scene: WechatScene.SESSION, imageData: File(path).readAsBytesSync());
+    }else if(type == "friend"){
+      // 这是朋友圈
+      await _wechat.shareImage(
+          scene: WechatScene.TIMELINE, imageData: File(path).readAsBytesSync());
+    }
   }
 
   void dispose() {

+ 51 - 0
lib/widgets/button_cancel.dart

@@ -37,3 +37,54 @@ class CancelButton extends StatelessWidget {
     );
   }
 }
+
+class FeelCancelButton extends StatelessWidget {
+  final VoidCallback callback;
+  final String content;
+  final double width;
+  final double height;
+  final double fontSize;
+  final Color color;
+  final Color borderColor;
+  final Color textColor;
+
+  static const finalColor = Color(0xffffffff);
+  static const finalBorderColor = Color(0xFFFFC400);
+  static const finalTextColor = Color(0xFFFFC400);
+
+  FeelCancelButton(
+      {@required this.callback,
+      @required this.content,
+      this.width = double.infinity,
+      this.height = 44,
+      this.fontSize = 28,
+      this.color = finalColor,
+      this.textColor = finalTextColor,
+      this.borderColor = finalBorderColor});
+
+  @override
+  Widget build(BuildContext context) {
+    return InkWell(
+      onTap: this.callback,
+      child: Container(
+        width: width,
+        height: height,
+        decoration: BoxDecoration(
+          color: color,
+          borderRadius: BorderRadius.all(Radius.circular(100)),
+          border: Border.all(
+            color: finalBorderColor,
+            width: .5,
+          ),
+        ),
+        child: Container(
+          alignment: Alignment.center,
+          child: Text(
+            content,
+            style: TextStyle(color: textColor, fontSize: 14),
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 25 - 6
lib/widgets/dialog/alert_dialog.dart

@@ -1,9 +1,18 @@
+import 'dart:async';
+
+import 'package:cached_network_image/cached_network_image.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:sport/bean/user_friend.dart';
+import 'package:sport/bean/user_info.dart';
+import 'package:sport/services/api/inject_api.dart';
+import 'package:sport/utils/toast.dart';
 import 'package:sport/widgets/button_cancel.dart';
 import 'package:sport/widgets/button_primary.dart';
 import 'package:sport/widgets/misc.dart';
 
+import '../space.dart';
+
 class CustomAlertDialog extends StatelessWidget {
   final String title;
   final Widget child;
@@ -12,11 +21,19 @@ class CustomAlertDialog extends StatelessWidget {
   final Function ok;
   final bool addClose;
 
-  CustomAlertDialog({this.title, this.child, this.textCancel = "取消", this.textOk = "确定", this.ok, this.addClose = false});
+  CustomAlertDialog({
+    this.title,
+    this.child,
+    this.textCancel = "取消",
+    this.textOk = "确定",
+    this.ok,
+    this.addClose = false,
+  });
 
   @override
   Widget build(BuildContext context) {
-    var _width = MediaQuery.of(context).size.width * (child != null ? 0.86 : 0.66);
+    var _width =
+        MediaQuery.of(context).size.width * (child != null ? 0.86 : 0.66);
     return Material(
       type: MaterialType.transparency,
       child: Center(
@@ -40,7 +57,8 @@ class CustomAlertDialog extends StatelessWidget {
                 Stack(
                   children: <Widget>[
                     Padding(
-                      padding: EdgeInsets.fromLTRB(0, child != null ? addClose ? 0 : 16.0 : 32.0, 0, 20),
+                      padding: EdgeInsets.fromLTRB(
+                          0, child != null ? addClose ? 0 : 16.0 : 32.0, 0, 20),
                       child: Text(
                         title,
                         style: Theme.of(context).textTheme.headline3,
@@ -51,7 +69,8 @@ class CustomAlertDialog extends StatelessWidget {
                 ),
               if (child != null)
                 ConstrainedBox(
-                    constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height - 100),
+                    constraints: BoxConstraints(
+                        maxHeight: MediaQuery.of(context).size.height - 100),
                     child: SingleChildScrollView(
                       child: child,
                     )),
@@ -72,8 +91,8 @@ class CustomAlertDialog extends StatelessWidget {
                       width: 16,
                     ),
                     Expanded(
-                      child: PrimaryButton(height: 35, callback: ok, content: textOk),
-                    )
+                        child: PrimaryButton(
+                            height: 35, callback: ok, content: textOk))
                   ],
                 ),
               )

+ 262 - 0
lib/widgets/dialog/scan_add_new_friend_dialog.dart

@@ -0,0 +1,262 @@
+import 'dart:async';
+
+import 'package:cached_network_image/cached_network_image.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:sport/bean/user_info.dart';
+import 'package:sport/services/api/inject_api.dart';
+import 'package:sport/utils/toast.dart';
+
+import '../button_cancel.dart';
+import '../button_primary.dart';
+import '../space.dart';
+
+/// 业务组件 只针对 扫码加好友情况下的弹窗...
+class ScanAddFriendCustomAlertDialog extends StatefulWidget {
+  final Function ok;
+  final bool addClose;
+  final UserInfo userInfo;
+  final String newFriendCode;
+  final bool isWaitAdd;
+  final Function goNext;
+
+  ScanAddFriendCustomAlertDialog(
+      {this.userInfo,
+      this.ok,
+      this.addClose = false,
+      this.newFriendCode,
+      this.isWaitAdd,
+      this.goNext});
+
+  @override
+  State<StatefulWidget> createState() {
+    // TODO: implement createState
+    return _ScanAddFriendCustomAlertDialog();
+  }
+}
+
+class _ScanAddFriendCustomAlertDialog
+    extends State<ScanAddFriendCustomAlertDialog> with InjectApi {
+  String okText = "申请中";
+  int dialogIndex = 0;
+  List<Function> dialogs;
+  Timer timerText;
+  Timer timerIsFriend;
+  @override
+  initState() {
+    super.initState();
+  }
+
+  initOkText() {
+    int cout = 0;
+    List<String> okTexts = ["申请中", "申请中.", "申请中..", "申请中..."];
+    timerText = Timer.periodic(const Duration(seconds: 1), (timer) {
+      //到时回调
+      setState(() {
+        okText = okTexts[cout % 3];
+      });
+      cout += 1;
+      if (cout >= 30) {
+        timer.cancel();
+        timer = null;
+      }
+    });
+  }
+
+  @override
+  void dispose() {
+    // TODO: implement dispose
+    super.dispose();
+    timerText?.cancel();
+//    timer = null;
+    timerIsFriend?.cancel();
+  }
+
+  void pollGetIsFriend(int uid) {
+    timerIsFriend = Timer.periodic(const Duration(seconds: 2), (timer) async {
+      //到时回调
+      var data = (await api.postIsFriend(uid)).data;
+      if (data) {
+        ToastUtil.show("添加成功");
+        timer.cancel();
+        timer = null;
+        Navigator.of(context).pop(false);
+      }
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    var _width = MediaQuery.of(context).size.width * 0.86;
+    Widget userInfoContent(bool isShowUserInfo) {
+      return Column(
+        children: <Widget>[
+          CircleAvatar(
+              backgroundImage: CachedNetworkImageProvider(
+                widget.userInfo?.avatar,
+              ),
+              radius: 35),
+          Space(
+            height: 8.0,
+          ),
+          Center(
+            child: Text(
+              "${widget.userInfo.name}",
+              style: Theme.of(context).textTheme.headline3,
+            ),
+          ),
+          Space(
+            height: 8.0,
+          ),
+          isShowUserInfo
+              ? Row(
+                  mainAxisAlignment: MainAxisAlignment.center,
+                  children: <Widget>[
+                    Row(
+                      children: <Widget>[
+                        Image.asset(
+                            "lib/assets/img/mine_icon_${widget.userInfo.gender == 1 ? "manl" : "girl"}_gray.png"),
+                        Space(
+                          width: 4,
+                        ),
+                        Text(
+                          widget.userInfo.gender == 1 ? "男" : "女",
+                          style:
+                              TextStyle(color: Color(0xff999999), fontSize: 12),
+                        ),
+                      ],
+                    ),
+                    if ((widget.userInfo.age ?? 0) != 0)
+                      Padding(
+                        padding: const EdgeInsets.only(left: 10.0),
+                        child: Text(
+                          "${widget.userInfo.age}岁",
+                          style:
+                              TextStyle(color: Color(0xff999999), fontSize: 12),
+                        ),
+                      ),
+                    if (widget.userInfo.city != null)
+                      Padding(
+                        padding: const EdgeInsets.only(left: 10.0),
+                        child: Text(
+                          "${widget.userInfo.province}${widget.userInfo.province == widget.userInfo.city ? '' : widget.userInfo.city}",
+                          style:
+                              TextStyle(color: Color(0xff999999), fontSize: 12),
+                        ),
+                      ),
+                  ],
+                )
+              : Text(
+                  "请求添加您为好友",
+                  style: Theme.of(context).textTheme.subtitle1,
+                ),
+        ],
+      );
+    }
+
+    Widget baseDialog(double width, Widget button, bool isShowUserInfo) {
+      return Center(
+        child: Container(
+          width: width,
+          decoration: BoxDecoration(
+            borderRadius: BorderRadius.all(Radius.circular(10.0)),
+            color: Colors.white,
+          ),
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              Align(
+                  alignment: Alignment.topRight,
+                  child: IconButton(
+                    icon: Image.asset("lib/assets/img/btn_close_big.png"),
+                    onPressed: () async {
+                      await api.postRejectFriend(widget.userInfo.id);
+                      Navigator.pop(context, false);
+                    },
+                  )),
+              ConstrainedBox(
+                  constraints: BoxConstraints(
+                      maxHeight: MediaQuery.of(context).size.height - 100),
+                  child: SingleChildScrollView(
+                      child: userInfoContent(isShowUserInfo))),
+              Padding(
+                padding: const EdgeInsets.all(16.0),
+                child: Row(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  children: <Widget>[
+                    Expanded(
+                      child: FeelCancelButton(
+                          height: 35,
+                          callback: () async {
+                            // 这里cancel好像不需要?
+                            // 代表是第三个框 可以 取消...
+                            if (!isShowUserInfo) {
+                              await api.postRejectFriend(widget.userInfo.id);
+                            }
+                            Navigator.of(context).pop(false);
+                          },
+                          content: "取消"),
+                    ),
+                    SizedBox(
+                      width: 16,
+                    ),
+                    Expanded(child: button)
+                  ],
+                ),
+              )
+            ],
+          ),
+        ),
+      );
+    }
+
+    Widget scanDialog(double width) {
+      return baseDialog(
+        width,
+        PrimaryButton(
+            height: 35,
+            callback: () async {
+              await api.getNewFriend(widget.newFriendCode);
+              setState(() {
+                dialogIndex = 1;
+                initOkText(); // 申请中的 setState
+                pollGetIsFriend(widget.userInfo.id); // 是否是好友的
+              });
+            },
+            content: "申请好友"),
+        true,
+      );
+    }
+
+    Widget confirmDialog(double width) {
+      return baseDialog(width,
+          FeelCancelButton(height: 35, content: okText, callback: () {}), true);
+    }
+
+    Widget confirmDialogOther(double width) {
+      return baseDialog(
+          width,
+          PrimaryButton(
+              height: 35,
+              callback: () async {
+                var data =
+                    (await api.postAcceptFriend(widget.userInfo.id)).data;
+                // 这里我不想去 争论什么 这里就是返回null的。 从来都没有用result 去 判断...
+                if (data == null) {
+                  ToastUtil.show("添加成功");
+                }
+                Navigator.of(context).pop(false);
+              },
+              content: "添加"),
+          false);
+    }
+
+    dialogs = [scanDialog, confirmDialog, confirmDialogOther];
+    return Material(
+      type: MaterialType.transparency,
+      child: widget.isWaitAdd != null
+          ? dialogs[2](_width)
+          : dialogs[dialogIndex](_width),
+    );
+  }
+}

+ 54 - 30
lib/widgets/dialog/share_popup.dart

@@ -54,16 +54,17 @@ class sharePopup extends StatefulWidget {
 }
 
 class _sharePopupState extends State<sharePopup> {
-  List<int> data = [3, 2, 1, 0];
-
+  List<int> data = [0, 1, 2, 3];
+  bool showCloseButton = true;
   Widget content(int e) {
     return Container(
-      width: MediaQuery.of(context).size.width * 1,
+//      width: MediaQuery.of(context).size.width * 1,
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: <Widget>[
           Container(
-            height: MediaQuery.of(context).size.height * 0.55,
+            padding: EdgeInsets.only(bottom: 20.0),
+//            height: MediaQuery.of(context).size.height * 0.7,
             width: MediaQuery.of(context).size.width * 0.7,
             decoration: BoxDecoration(
                 borderRadius: BorderRadius.all(Radius.circular(20.0)),
@@ -157,7 +158,7 @@ class _sharePopupState extends State<sharePopup> {
                         Navigator.pop(context);
                       }
                       NavigatorUtil.goPage(
-                          context, (context) => shareAchievementPage());
+                          context, (context) => ShareAchievementPage());
                     },
                   ),
                 ),
@@ -187,9 +188,28 @@ class _sharePopupState extends State<sharePopup> {
                 ),
               ],
             ),
+//            padding: EdgeInsets.only(bottom: 20.0),
           ),
-          Space(
-            height: 40.0,
+          Padding(
+            padding: EdgeInsets.only(top: 20.0),
+            child: data.length > 0 && 0 == data.indexOf(e)
+                ? InkWell(
+                    child: Image.asset("lib/assets/img/pop_share_chose.png"),
+                    onTap: () {
+                      if (data.length != 1) {
+                        data.removeAt(0);
+                        setState(() {
+                          data = data;
+                        });
+                      } else {
+                        Navigator.of(context).pop();
+                      }
+                    },
+                  )
+                : Container(
+                    width: 35.0,
+                    height: 35.0,
+                  ),
           )
         ],
       ),
@@ -205,31 +225,35 @@ class _sharePopupState extends State<sharePopup> {
       child: Column(
         mainAxisAlignment: MainAxisAlignment.start,
         children: <Widget>[
-          Container(
-            height: MediaQuery.of(context).size.height * 0.8,
-            // stack 需要一个高度进行搞一搞
-            child: Stack(
-              children: data.map((e) {
-                return Positioned(
-                    left: (e + 1) * 5.0,
-                    bottom: (e + 1) * 5.0,
-                    child: content(e));
-              }).toList(),
+          Expanded(
+            child: Center(
+              // stack 需要一个高度进行搞一搞
+              child: Stack(
+                children: data.reversed.map((e) {
+                  return Transform.translate(
+                      offset: Offset((data.indexOf(e) + 1) * 5.0,
+                          -(data.indexOf(e)) * 5.0),
+//                      padding: EdgeInsets.only(
+//                          right: (data.indexOf(e)) * 10.0,
+//                          top: (data.indexOf(e)) * 10.0),
+                      child: content(e));
+                }).toList(),
+              ),
             ),
           ),
-          if (data.length > 0)
-            InkWell(
-              child: Image.asset("lib/assets/img/pop_share_chose.png"),
-              onTap: () {
-                if (data.length != 1) {
-                  setState(() {
-                    data = data.sublist(0, data.length - 1);
-                  });
-                } else {
-                  Navigator.of(context).pop();
-                }
-              },
-            )
+//          if (data.length > 0)
+//            InkWell(
+//              child: Image.asset("lib/assets/img/pop_share_chose.png"),
+//              onTap: () {
+//                if (data.length != 1) {
+//                  setState(() {
+//                    data = data.sublist(0, data.length - 1);
+//                  });
+//                } else {
+//                  Navigator.of(context).pop();
+//                }
+//              },
+//            )
         ],
       ),
     );

+ 79 - 25
lib/widgets/menu_bar.dart

@@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:image_picker/image_picker.dart';
 import 'package:multi_image_picker/multi_image_picker.dart';
+import 'package:sport/bean/message.dart';
+import 'package:sport/services/api/inject_api.dart';
 import 'package:sport/widgets/button_primary.dart';
 import 'package:sport/widgets/decoration.dart';
 
@@ -14,9 +16,13 @@ class MenuBar extends StatefulWidget {
   final Function closeMenuCallBack;
   final Widget messageList;
   final Function scrollToBottom;
+  final MenuIdentity menuIdentity;
 
   MenuBar(this.messageList,
-      {this.inputField, this.closeMenuCallBack, this.scrollToBottom});
+      {@required this.menuIdentity,
+      this.inputField,
+      this.closeMenuCallBack,
+      this.scrollToBottom});
 
   @override
   State<StatefulWidget> createState() {
@@ -25,7 +31,8 @@ class MenuBar extends StatefulWidget {
   }
 }
 
-class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver {
+class _MenuBarState extends State<MenuBar>
+    with WidgetsBindingObserver, InjectApi {
   GlobalKey _myKey = new GlobalKey(); // 用来定位Message位置
 
   List<Asset> imageList = []; // 选图片的列表
@@ -53,21 +60,15 @@ class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver {
 
     initEmoji();
 
-    _controller = TextEditingController.fromValue(TextEditingValue(
-        // 设置内容
-        text: _textFieldValue,
-        // 保持光标在最后
-        selection: TextSelection.fromPosition(TextPosition(
-            affinity: TextAffinity.upstream,
-            offset: _textFieldValue.length))));
+    _controller = TextEditingController();
 
-    _controller.addListener(() {
-      print("----------------------------------------------------------");
-      setState(() {
-        _textFieldValue = _controller.text;
-      });
-      print("linsten");
-    });
+//    _controller.addListener(() {
+//      print("----------------------------------------------------------");
+//      setState(() {
+//        _textFieldValue = _controller.text;
+//      });
+//      print("linsten");
+//    });
 
     _focusNode.addListener(() {
       if (_focusNode.hasFocus) {
@@ -127,15 +128,27 @@ class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver {
 
     void getPhoto() async {
       try {
+        // 拍完直接发...
         final pickedFile =
             await new ImagePicker().getImage(source: ImageSource.camera);
-        setState(() {
-          if (pickedFile != null) {
-            _image = File(pickedFile.path);
-          } else {
-            print('No image selected.');
-          }
-        });
+//        setState(() {
+//          if (pickedFile != null) {
+//            _image = File(pickedFile.path);
+//          } else {
+//            print('No image selected.');
+//          }
+//        });
+        // 如果是聊天 直接发 ...
+        if (widget.menuIdentity.menuScene == "chat") {
+          // 先上传
+          String url = (await api.postChatUpload(File(pickedFile.path))).data;
+          MessageInstance message = (await api.postChatSend(
+                  widget.menuIdentity.userId,
+                  "image",
+                  '{ "data":{ "url":"$url" }}'))
+              .data;
+          print("$message -------------------------------");
+        }
       } on Exception catch (e) {
         error = e.toString();
       }
@@ -191,7 +204,15 @@ class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver {
                 onTap: () {
                   String intPutString = _controller.text +
                       String.fromCharCode(data[index]["unicode"]);
-                  _controller.text = intPutString;
+
+                  var content = intPutString;
+                  _controller.value = TextEditingValue(
+                      // 设置内容
+                      text: content,
+                      // 保持光标在最后
+                      selection: TextSelection.fromPosition(TextPosition(
+                          affinity: TextAffinity.downstream,
+                          offset: content.length)));
                 },
                 child: Center(
                   child: Text(
@@ -259,6 +280,7 @@ class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver {
               });
               _focusNode.unfocus();
             },
+            // 列表在这里...
             child: widget.messageList,
           ),
         ),
@@ -340,7 +362,19 @@ class _MenuBarState extends State<MenuBar> with WidgetsBindingObserver {
                         ),
                         Expanded(
                           flex: 3,
-                          child: PrimaryButton(content: "发送", callback: () {}),
+                          child: PrimaryButton(
+                              content: "发送",
+                              callback: () async {
+                                MessageInstance message = (await api.postChatSend(
+                                        widget.menuIdentity.userId,
+                                        "text",
+                                            '{"text":"${_controller.text}"}'))
+                                    .data;
+//                                print(
+//                                    "${json.encode('{ "data":{ "text":"${_controller.text}" }}')}");
+                                // 这里可能传不了 callback 所有需要的值都在menu bar里面 只能通过传 不同的 场景 进行 不同的操作
+                                //widget.sendCallBack();
+                              }),
                         ),
                       ],
                     ),
@@ -366,3 +400,23 @@ class GetMenuController {
     );
   }
 }
+
+// 自己内部传的 bean 用来鉴别 是什么 场景使用的 menuBar
+class MenuIdentity {
+  String menuScene; // 当前的MenuBar 所属的场景在哪 比如 聊天 chat / 社区 social / 反馈 feedBack 等
+  int userId; // Chat的时候需要的userId
+
+  MenuIdentity({this.menuScene, this.userId});
+
+  MenuIdentity.fromJson(Map<String, dynamic> json) {
+    menuScene = json['menuScene'];
+    userId = json['userId'];
+  }
+
+  Map<String, dynamic> toJson() {
+    final Map<String, dynamic> data = new Map<String, dynamic>();
+    data['menuScene'] = this.menuScene;
+    data['userId'] = this.userId;
+    return data;
+  }
+}

+ 228 - 82
lib/widgets/menu_share_bottom.dart

@@ -1,104 +1,250 @@
+import 'dart:ui';
+
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
 import 'package:flutter/services.dart';
+import 'package:image_gallery_saver/image_gallery_saver.dart';
+import 'package:sport/bean/post.dart';
+import 'package:sport/pages/social/post_share_page.dart';
+import 'package:sport/router/navigator_util.dart';
 import 'package:sport/utils/toast.dart';
+import 'package:path_provider/path_provider.dart';
+import 'package:sport/sharesdk/tencent.dart';
+import 'package:sport/sharesdk/wechat.dart';
+import 'dart:io';
+import 'dart:typed_data';
 
-Widget menuShareBottomContent(BuildContext context,
-    {String url, bool hasDownload}) {
-  List<Map<String, dynamic>> map = [
-    {"value": "微信", "url": "share_icon_wechat", "callBack": () {}},
-    {"value": "朋友圈", "url": "share_icon_wechatmonents", "callBack": () {}},
-    {"value": "QQ", "url": "share_icon_qq", "callBack": () {}},
-    {"value": "社区", "url": "share_icon_community", "callBack": () {}},
-    {
-      "value": "复制链接",
-      "url": "share_icon_link",
-      "callBack": () {
-        Clipboard.setData(ClipboardData(text: url));
-        ToastUtil.show("复制成功");
-      }
-    },
-    {"value": "下载图片", "url": "share_icon_download", "callBack": () {}},
-  ];
+class MenuShareBottomContent extends StatefulWidget {
+  final String url;
+  final bool hasDownload;
+  final GlobalKey poster;
+  final String shareType;
+  final Post post;
+
+  MenuShareBottomContent(this.shareType,
+      {this.post, this.url = "", this.hasDownload = false, this.poster});
+  @override
+  State<StatefulWidget> createState() {
+    // TODO: implement createState
+    return _MenuShareBottomContentState();
+  }
+}
+// shareType : Img / Link / Social
+
+class _MenuShareBottomContentState extends State<MenuShareBottomContent>
+    with TencentMixin, WechatMixin {
+  List<Map<String, dynamic>> map;
+  Uint8List postInstance;
+  @override
+  void initState() {
+    // TODO: implement initState
+    super.initState();
+    map = [
+      {
+        "value": "微信",
+        "url": "share_icon_wechat",
+        "callBack": () async {
+          if (widget.shareType == "Img") {
+            String path = await initFile();
+            wechatShareImage(path, "chat");
+          } else {
+            print("-----");
+          }
+        }
+      },
+      {
+        "value": "朋友圈",
+        "url": "share_icon_wechatmonents",
+        "callBack": () async {
+          if (widget.shareType == "Img") {
+            String path = await initFile();
+            wechatShareImage(path, "friend");
+          } else {
+            print("-----");
+          }
+        }
+      },
+      {
+        "value": "QQ",
+        "url": "share_icon_qq",
+        "callBack": () async {
+          if (widget.shareType == "Img") {
+            String path = await initFile();
+            qqShare(path);
+          } else {
+            print("-----");
+          }
+        }
+      },
+      {
+        "value": "社区",
+        "url": "share_icon_community",
+        "callBack": () {
+          if (widget.shareType == "social") {
+            NavigatorUtil.goPage(
+                context, (context) => PostSharePage(widget.post));
+          }
+        }
+      },
+      {
+        "value": "社区好友",
+        "url": "share_icon_friends",
+        "callBack": () {
+          if (widget.shareType == "social") {
+            NavigatorUtil.goPage(
+                context, (context) => PostShareFriendsPage(widget.post));
+          }
+        }
+      },
+      {
+        "value": "复制链接",
+        "url": "share_icon_link",
+        "callBack": () {
+          Clipboard.setData(ClipboardData(text: widget.url));
+          ToastUtil.show("复制成功");
+        }
+      },
+      {
+        "value": "下载图片",
+        "url": "share_icon_download",
+        "callBack": () {
+          _capture().then((file) async {}).whenComplete(() {
+            print("Complete-------------------------------------------------");
+          });
+        }
+      },
+    ];
+
+    if (widget.hasDownload == null || !widget.hasDownload) {
+      map.removeLast();
+    }
+
+    if (widget.url == null) {
+      map.removeAt(5);
+    }
+    // 社区暂时先把前面的隐藏掉
+    if (widget.shareType == "social") {
+      map = map.sublist(map.length - 2, map.length);
+    }
+  }
+
+  // 封装成灵活的更通用一点...
+  Future<String> initFile() async {
+    Uint8List pngBytes = await initFileUint8List();
+
+    String sTempDir = (await getTemporaryDirectory()).path;
+    bool isDirExist = await Directory(sTempDir).exists();
+    if (!isDirExist) {
+      Directory(sTempDir).create();
+    }
+    // QQ分享需要一个确实存在的图片
+    File file = await File(
+            sTempDir + "/poster-temp-${DateTime.now().millisecondsSinceEpoch}")
+        .writeAsBytes(pngBytes);
+    return file.path;
+  }
 
-  if (!hasDownload) {
-    map.removeLast();
+  Future<Uint8List> initFileUint8List() async {
+    try {
+      RenderRepaintBoundary boundary =
+          widget.poster.currentContext.findRenderObject();
+      //boundary.toImage()转化为ui.Image对象,不会自动为包裹的组件添加背景,不设置可能会缺失背景
+      var image = await boundary.toImage(pixelRatio: window.devicePixelRatio);
+      //将image转化为byteData
+      ByteData byteData = await image.toByteData(format: ImageByteFormat.png);
+      //这个对象就是图片数据
+      Uint8List pngBytes = byteData.buffer.asUint8List();
+
+      print("成功生成图片 ----------------------------------------");
+      return pngBytes;
+    } catch (e) {
+      print(e);
+    }
+    return null;
   }
 
-  if (url == null) {
-    map.removeAt(4);
+  Future<File> _capture() async {
+    Uint8List pngBytes = await initFileUint8List();
+    final result = await ImageGallerySaver.saveImage(pngBytes); //这个是核心的保存图片的插件
+    print(result);
+    ToastUtil.show("下载成功");
   }
 
-  return SizedBox(
-    child: Container(
-      margin: EdgeInsets.fromLTRB(24, 37, 24, 17),
-      child: Column(
-        children: <Widget>[
-          Row(
-            children: <Widget>[
-              Expanded(
-                child: Divider(
-                  endIndent: 12.0,
+  @override
+  Widget build(BuildContext context) {
+    // TODO: implement build
+    return SizedBox(
+      child: Container(
+        margin: EdgeInsets.fromLTRB(24, 27, 24, 17),
+        child: Column(
+          children: <Widget>[
+            Row(
+              children: <Widget>[
+                Expanded(
+                  child: Divider(
+                    endIndent: 12.0,
+                  ),
                 ),
-              ),
-              Text(
-                "分享至",
-                style: Theme.of(context).textTheme.bodyText2,
-              ),
-              Expanded(
-                child: Divider(
-                  indent: 12.0,
+                Text(
+                  "分享至",
+                  style: Theme.of(context).textTheme.bodyText2,
                 ),
-              )
-            ],
-          ),
-          SizedBox(
-            height: 12.0,
-          ),
-          Expanded(
-            child: GridView(
-                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
-                    crossAxisCount: 4,
-                    crossAxisSpacing: 12.0,
-                    mainAxisSpacing: 12.0),
-                children: map
-                    .map(
-                      (e) => InkWell(
-                        child: Column(
-                          crossAxisAlignment: CrossAxisAlignment.center,
-                          children: <Widget>[
-                            Image.asset(
-                              "lib/assets/img/${e["url"]}.png",
-                              width: 44.0,
-                              height: 44.0,
-                            ),
-                            Text(
-                              "${e["value"]}",
-                              style: Theme.of(context).textTheme.subtitle2,
-                            )
-                          ],
+                Expanded(
+                  child: Divider(
+                    indent: 12.0,
+                  ),
+                )
+              ],
+            ),
+            SizedBox(
+              height: 12.0,
+            ),
+            Expanded(
+              child: GridView(
+                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
+                      crossAxisCount: 4,
+                      crossAxisSpacing: 12.0,
+                      mainAxisSpacing: 12.0),
+                  children: map
+                      .map(
+                        (e) => InkWell(
+                          child: Column(
+                            crossAxisAlignment: CrossAxisAlignment.center,
+                            children: <Widget>[
+                              Image.asset(
+                                "lib/assets/img/${e["url"]}.png",
+                                width: 44.0,
+                                height: 44.0,
+                              ),
+                              Text(
+                                "${e["value"]}",
+                                style: Theme.of(context).textTheme.subtitle2,
+                              )
+                            ],
+                          ),
+                          onTap: () {
+                            e["callBack"]();
+                          },
                         ),
-                        onTap: () {
-                          e["callBack"]();
-                        },
-                      ),
-                    )
-                    .toList()),
-          )
-        ],
+                      )
+                      .toList()),
+            )
+          ],
+        ),
       ),
-    ),
-    height: 250.0,
-  );
+      height: 232.0,
+    );
+  }
 }
 
-Future<bool> menuShareBottom(BuildContext context,
-    {String url, bool hasDownload}) {
+Future<bool> menuShareBottom(BuildContext context, String shareType,
+    {Post post, String url, bool hasDownload, GlobalKey poster}) {
   return showModalBottomSheet(
     context: context,
     builder: (context) {
-      return menuShareBottomContent(context,
-          url: url, hasDownload: hasDownload);
+      return MenuShareBottomContent(shareType,
+          post: post, url: url, hasDownload: hasDownload, poster: poster);
     },
     backgroundColor: Colors.white,
     elevation: 10,