diff --git a/circle_app/assets/images/circle/location.png b/circle_app/assets/images/circle/location.png index a2472eb..801db5e 100644 Binary files a/circle_app/assets/images/circle/location.png and b/circle_app/assets/images/circle/location.png differ diff --git a/circle_app/lib/app/call_out/logic.dart b/circle_app/lib/app/call_out/logic.dart index 52aa99d..5dc3c6d 100644 --- a/circle_app/lib/app/call_out/logic.dart +++ b/circle_app/lib/app/call_out/logic.dart @@ -38,12 +38,6 @@ class Call_outLogic extends GetxController { videoPlayerController?.dispose(); } - @override - void dispose() { - videoPlayerController?.dispose(); - super.dispose(); - } - @override void onInit() async { super.onInit(); diff --git a/circle_app/lib/app/circle/state.dart b/circle_app/lib/app/circle/state.dart index c2b236a..0015898 100644 --- a/circle_app/lib/app/circle/state.dart +++ b/circle_app/lib/app/circle/state.dart @@ -5,3 +5,170 @@ class CircleState { ///Initialize variables } } + + +class Lists { + List? album; + Chat? chat; + String? content; + int? id; + bool? isQueen; + User? user; + + Lists( + {this.album, this.chat, this.content, this.id, this.isQueen, this.user}); + + Lists.fromJson(Map json) { + if (json['album'] != null) { + album = []; + json['album'].forEach((v) { + album!.add(new Album.fromJson(v)); + }); + } + chat = json['chat'] != null ? new Chat.fromJson(json['chat']) : null; + content = json['content']; + id = json['id']; + isQueen = json['is_queen']; + user = json['user'] != null ? new User.fromJson(json['user']) : null; + } + + Map toJson() { + final Map data = new Map(); + if (this.album != null) { + data['album'] = this.album!.map((v) => v.toJson()).toList(); + } + if (this.chat != null) { + data['chat'] = this.chat!.toJson(); + } + data['content'] = this.content; + data['id'] = this.id; + data['is_queen'] = this.isQueen; + if (this.user != null) { + data['user'] = this.user!.toJson(); + } + return data; + } +} + +class Album { + int? type; + String? url; + + Album({this.type, this.url}); + + Album.fromJson(Map json) { + type = json['type']; + url = json['url']; + } + + Map toJson() { + final Map data = new Map(); + data['type'] = this.type; + data['url'] = this.url; + return data; + } +} + +class Chat { + String? accountId; + int? count; + List? users; + + Chat({this.accountId, this.count, this.users}); + + Chat.fromJson(Map json) { + accountId = json['account_id']; + count = json['count']; + if (json['users'] != null) { + users = []; + json['users'].forEach((v) { + users!.add(new Users.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['account_id'] = this.accountId; + data['count'] = this.count; + if (this.users != null) { + data['users'] = this.users!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Users { + String? avatar; + int? id; + String? nickname; + + Users({this.avatar, this.id, this.nickname}); + + Users.fromJson(Map json) { + avatar = json['avatar']; + id = json['id']; + nickname = json['nickname']; + } + + Map toJson() { + final Map data = new Map(); + data['avatar'] = this.avatar; + data['id'] = this.id; + data['nickname'] = this.nickname; + return data; + } +} + +class User { + String? avatar; + String? city; + int? gender; + int? id; + double? lat; + double? lng; + String? nickname; + int? orientation; + int? role; + int? vip; + + User( + {this.avatar, + this.city, + this.gender, + this.id, + this.lat, + this.lng, + this.nickname, + this.orientation, + this.role, + this.vip}); + + User.fromJson(Map json) { + avatar = json['avatar']; + city = json['city']; + gender = json['gender']; + id = json['id']; + lat = json['lat'] ?? 0.0; + lng = json['lng'] ?? 0.0; + nickname = json['nickname']; + orientation = json['orientation']; + role = json['role']; + vip = json['vip']; + } + + Map toJson() { + final Map data = new Map(); + data['avatar'] = this.avatar; + data['city'] = this.city; + data['gender'] = this.gender; + data['id'] = this.id; + data['lat'] = this.lat; + data['lng'] = this.lng; + data['nickname'] = this.nickname; + data['orientation'] = this.orientation; + data['role'] = this.role; + data['vip'] = this.vip; + return data; + } +} \ No newline at end of file diff --git a/circle_app/lib/app/circle/widgets/info_list_view.dart b/circle_app/lib/app/circle/widgets/info_list_view.dart index 619bc15..1a48881 100644 --- a/circle_app/lib/app/circle/widgets/info_list_view.dart +++ b/circle_app/lib/app/circle/widgets/info_list_view.dart @@ -1,19 +1,45 @@ +import 'dart:ffi'; + +import 'package:cached_network_image/cached_network_image.dart'; import 'package:circle_app/app/circle/logic.dart'; +import 'package:circle_app/app/circle/state.dart'; +import 'package:circle_app/app/circle/widgets/list_logic.dart'; +import 'package:circle_app/app/circle/widgets/video_item.dart'; +import 'package:circle_app/router/app_routers.dart'; import 'package:circle_app/util/util.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:get/get_core/src/get_main.dart'; +import 'package:get/get.dart'; class InfoListView extends StatelessWidget { + // Get.lazyPut(() => ListLogic()); + CircleLogic logic; Circle bean; int index; - final ScrollController scrollController = ScrollController(); - InfoListView(this.index,this.bean,this.logic, {super.key}); + InfoListView(this.index, this.bean, this.logic, {super.key}); + + List genderList = ['男', '女,' 'MTF', 'FTM', 'CD', '酷儿']; + List orientationList = [ + '异性恋', + '同性恋', + '双性恋', + '泛性恋', + '无性恋', + '智性恋', + '性单恋' + ]; + List roleList = ['Sado', 'Maso', 'Dom', 'Sub', 'Switch']; + @override Widget build(BuildContext context) { + Get.lazyPut(() => ListLogic()); + final listsLg = Get.find(); + listsLg.loadCallOutListData(bean.id.toString()); + TextSpan descSpan; TextSpan span; if (bean.intro.length > 60) { @@ -37,7 +63,6 @@ class InfoListView extends StatelessWidget { ); } - List urlList = bean.lastJoinUsers; List widgets = []; int i = 0; @@ -49,149 +74,172 @@ class InfoListView extends StatelessWidget { i++; }); - return ClipRRect( - borderRadius: BorderRadius.circular(10.sp), - child: Container( - width: Get.width, - margin: EdgeInsets.only(left: index > 0 ? 4.sp : 0, right: 4.sp), - child: Stack( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(10.sp), - child: SizedBox( + return GetBuilder( + assignId: true, + builder: (listLogic) { + return ClipRRect( + borderRadius: BorderRadius.circular(10.sp), + child: Container( + width: Get.width, + margin: + EdgeInsets.only(left: index > 0 ? 4.sp : 0, right: 4.sp), + child: Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(10.sp), + child: SizedBox( + width: Get.width, + child: Image.asset( + getCircleImage('circle_bg'), + fit: BoxFit.fill, + ), + )), + Container( + height: Get.height, width: Get.width, - child: Image.asset( - getCircleImage('circle_bg'), - fit: BoxFit.fill, - ), - )), - Container( - height: Get.height, - width: Get.width, - margin: EdgeInsets.only(top: 72.sp), - child: ListView.builder( - controller: scrollController, - itemCount: 10, - itemBuilder: (context, index) { - if (index == 0) { - return Container( - padding: EdgeInsets.only(left: 12.sp, right: 12.sp), - height: 88.sp, - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.fill, - image: AssetImage( - getCircleImage('circle_desc')))), - child: Container( - margin: EdgeInsets.only(top: 14.sp), - child: Column( - children: [ - Container( - width: Get.width, - height: 30.sp, - child: GestureDetector( - onTap: () { - _showTextContentDialog( - context, bean.intro); - }, - child: RichText( - overflow: TextOverflow.fade, - maxLines: 2, - text: TextSpan(children: [ - descSpan, - span - ])), - ), - ), - Container( - child: Row( - children: [ - Row( + margin: EdgeInsets.only(top: 72.sp), + child: RefreshIndicator( + onRefresh: () async { + listLogic.refreshData(); + }, + child: listLogic.lists.isEmpty + ? loaddingWidget(true) + : ListView.builder( + scrollDirection: Axis.vertical, + controller: listLogic.scrollController, + itemCount: listLogic.lists.length + 2, + itemBuilder: (context, index) { + if (index == 0) { + return Container( + padding: EdgeInsets.only( + left: 12.sp, right: 12.sp), + height: 88.sp, + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage(getCircleImage( + 'circle_desc')))), + child: Container( + margin: EdgeInsets.only(top: 14.sp), + child: Column( children: [ Container( + width: Get.width, height: 30.sp, - width: 30.0.sp + - 15.sp * - (widgets.length - 1.sp), - child: Stack( - children: widgets, + child: GestureDetector( + onTap: () { + _showTextContentDialog( + context, bean.intro); + }, + child: RichText( + overflow: TextOverflow.fade, + maxLines: 2, + text: TextSpan( + children: [ + descSpan, + span + ])), ), ), - SizedBox( - width: 8.sp, + Container( + child: Row( + children: [ + Row( + children: [ + Container( + height: 30.sp, + width: 30.0.sp + + 15.sp * + (widgets.length - + 1.sp), + child: Stack( + children: widgets, + ), + ), + SizedBox( + width: 8.sp, + ), + Text( + '${convertToTenThousand(bean.viewTotal)}圈友', + style: TextStyle( + color: Colors.white, + fontSize: 12.sp), + ), + SizedBox( + width: 8.sp, + ), + Image.asset( + getCircleImage('play'), + width: 20.sp, + ) + ], + ) + ], + ), ), - Text( - '${convertToTenThousand(bean.viewTotal)}圈友', - style: TextStyle( - color: Colors.white, - fontSize: 12.sp), - ), - SizedBox( - width: 8.sp, - ), - Image.asset( - getCircleImage('play'), - width: 20.sp, - ) ], - ) - ], - ), + ), + ), + ); + } else if (listLogic.lists.length + 1 > + index) { + Lists lists = listLogic.lists[index - 1]; + if (lists.isQueen!) { + return vipDynamicItem(lists); + } else { + return normalDynamicItem(lists); + } + } else { + return loaddingWidget( + listLogic.callOutMore); + } + }), + ), + ), + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10.sp), + topRight: Radius.circular(10.sp)), + child: Container( + padding: EdgeInsets.only(left: 12.sp, right: 12.sp), + height: 72.sp, + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage( + getCircleImage('top_circle_bg')))), + child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width: 42.sp, + height: 42.sp, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Color(0xFF71F3F2), + Color(0xFFF558FF), + ], + stops: [0.0365, 0.9427], ), - ], + ), + padding: EdgeInsets.all(1.sp), + child: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: Image.network( + bean.image, + width: 40.sp, + height: 40.sp, + ), + ), ), - ), - ); - } else if (index == 1) { - return vipDynamicItem(bean); - } else { - return normalDynamicItem(bean); - } - }), - ), - ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10.sp), - topRight: Radius.circular(10.sp)), - child: Container( - padding: EdgeInsets.only(left: 12.sp, right: 12.sp), - height: 72.sp, - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.fill, - image: - AssetImage(getCircleImage('top_circle_bg')))), - child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - width: 42.sp, - height: 42.sp, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.0), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Color(0xFF71F3F2), - Color(0xFFF558FF), - ], - stops: [0.0365, 0.9427], - ), - ), - padding: EdgeInsets.all(1.sp), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: Image.network( - bean.image, - width: 40.sp, - height: 40.sp, - ), - ), - ), - Expanded( - child: Container( - padding: EdgeInsets.only(left: 8.sp, top: 12.sp), + Expanded( + child: Container( + padding: + EdgeInsets.only(left: 8.sp, top: 12.sp), // alignment: Alignment., height: 72.sp, child: Column( @@ -218,32 +266,35 @@ class InfoListView extends StatelessWidget { ], ), )), - GestureDetector( - onTap: () { - if (bean.isJoin) { - _showOutCircleDialog(context, logic, bean); - } else { - logic.outCircle( - bean.id.toString(), bean.isJoin); - } - }, - child: Image.asset( - getCircleImage('add'), - width: 77.sp, - ), - ) - ], - ), - )), - Positioned( - top: 70.sp, - child: Image.asset( - getCircleImage('circle_line'), - width: Get.width, - )), - ], - ))); + GestureDetector( + onTap: () { + if (bean.isJoin) { + _showOutCircleDialog(context, logic, bean); + } else { + logic.outCircle( + bean.id.toString(), bean.isJoin); + } + }, + child: Image.asset( + getCircleImage('add'), + width: 77.sp, + ), + ) + ], + ), + )), + Positioned( + top: 70.sp, + child: Image.asset( + getCircleImage('circle_line'), + width: Get.width, + )), + ], + ))); + }, + ); } + void _showTextContentDialog(BuildContext context, String msg) { showDialog( context: context, @@ -304,35 +355,39 @@ class InfoListView extends StatelessWidget { ); } - ///至尊喊话 - vipDynamicItem(Circle bean) { + vipDynamicItem(Lists bean) { Text descText = Text( - bean.intro, + bean.content!, style: TextStyle(color: Colors.white, fontSize: 14.sp), maxLines: 2, ); - List urlList = bean.lastJoinUsers; + // List urlList = bean.lastJoinUsers; List widgets = []; int index = 0; - urlList.forEach((element) { + bean.chat!.users!.forEach((element) { widgets.add(Positioned( left: 12.sp * index, - child: circleWidget(element.avatar, width: 24), + child: circleWidget(element.avatar!, width: 24), )); index++; }); - widgets.add(Positioned( - left: 12.sp * urlList.length, - child: Image.asset( - getCircleImage('more'), - width: 24.sp, - ))); + if (widgets.isNotEmpty) { + widgets.add(Positioned( + left: 12.sp * bean.chat!.users!.length, + child: Image.asset( + getCircleImage('more'), + width: 24.sp, + ))); + } + + double widgetHeight = 250.sp + contentHeight(bean.content!); + return Container( margin: EdgeInsets.only(top: 10.sp), width: Get.width, - height: 279.sp, + height: widgetHeight, decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.fill, @@ -366,83 +421,86 @@ class InfoListView extends StatelessWidget { children: [ GestureDetector( child: Stack( - alignment: Alignment.center, - children: [ - Image.asset( - getCircleImage('avatar_bg'), - width: 42.sp, - ), - ClipOval( - child: Image.network( - 'https://p3-passport.byteimg.com/img/user-avatar/eb429d4dbb3c246f580a6f7894f2b246~100x100.awebp', - width: 40.sp, - height: 40.sp, - fit: BoxFit.fill, - ), - ) - ], - )), + alignment: Alignment.center, + children: [ + Image.asset( + getCircleImage('avatar_bg'), + width: 42.sp, + ), + ClipOval( + child: Image.network( + bean.user!.avatar!, + width: 40.sp, + height: 40.sp, + fit: BoxFit.fill, + ), + ) + ], + )), Expanded( child: Container( - padding: EdgeInsets.only(left: 8.sp, top: 12.sp), - alignment: Alignment.centerLeft, - height: 72.sp, - child: Column( + padding: EdgeInsets.only(left: 8.sp, top: 12.sp), + alignment: Alignment.centerLeft, + height: 72.sp, + child: Column( + children: [ + Row( children: [ - Row( - children: [ - Text( - '圈子名称', - style: TextStyle( - color: Colors.white, - fontSize: 18.sp, - fontWeight: FontWeight.w600), - ), - SizedBox( - width: 8.sp, - ), - Image.asset( - getCircleImage('vip'), - width: 36.sp, - ) - ], + Text( + bean.user!.nickname!, + style: TextStyle( + color: Colors.white, + fontSize: 18.sp, + fontWeight: FontWeight.w600), ), SizedBox( - height: 4.sp, - ), - Row( - children: [ - Container( - alignment: Alignment.center, - height: 18.sp, - padding: - EdgeInsets.only(left: 6.sp, right: 6.sp), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(9.sp), - gradient: const LinearGradient( - begin: Alignment(0.25, 0.5), - end: Alignment(0.75, 0.5), - colors: [ - Color(0xff8DFFF8), - Color(0xffB5D3FF) - ])), - child: Text( - '男.33.DOM.异性恋', - style: TextStyle( - color: Colors.black, - fontSize: 12.sp, - ), - ), - ) - ], + width: 8.sp, ), + bean.user!.vip! == 0 + ? Container() + : Image.asset( + getCircleImage('vip'), + width: 36.sp, + ) ], ), - )), + SizedBox( + height: 4.sp, + ), + Row( + children: [ + Container( + alignment: Alignment.center, + height: 18.sp, + padding: + EdgeInsets.only(left: 6.sp, right: 6.sp), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(9.sp), + gradient: const LinearGradient( + begin: Alignment(0.25, 0.5), + end: Alignment(0.75, 0.5), + colors: [ + Color(0xff8DFFF8), + Color(0xffB5D3FF) + ])), + child: Text( + '${genderList[bean.user!.gender!]}.${'33'}.${roleList[bean.user!.role!]}.${orientationList[bean.user!.orientation!]}', + style: TextStyle( + color: Colors.black, + fontSize: 12.sp, + ), + ), + ) + ], + ), + ], + ), + )), ], ), ), Container( + alignment: Alignment.topLeft, // margin: EdgeInsets.only(top: 4.sp), child: descText, ), @@ -455,7 +513,7 @@ class InfoListView extends StatelessWidget { crossAxisCount: 3, //横轴三个子widget crossAxisSpacing: 8.sp, childAspectRatio: 1.0 //宽高比为1时,子widget - ), + ), children: [ ClipRRect( borderRadius: BorderRadius.circular(6.sp), @@ -490,19 +548,29 @@ class InfoListView extends StatelessWidget { colors: [Color(0xff261240), Color(0xff122D40)])), child: Row( children: [ - Container( - width: 24 + 12.sp * widgets.length - 12.sp, - height: 24.sp, - child: Stack(children: widgets), - ), + widgets.isNotEmpty + ? Container( + width: 24 + 12.sp * widgets.length - 12.sp, + height: 24.sp, + child: Stack(children: widgets), + ) + : Container(), SizedBox( width: 4.sp, ), - Expanded( - child: Text( - '1位圈友已私聊', - style: TextStyle(color: Colors.white, fontSize: 12.sp), - )), + widgets.isNotEmpty + ? Expanded( + child: Text( + '${bean.chat!.count!}位圈友已私聊', + style: TextStyle( + color: Colors.white, fontSize: 12.sp), + )) + : Expanded( + child: Text( + '赶紧成为第一位私聊ta的圈友吧', + style: TextStyle( + color: Colors.white, fontSize: 12.sp), + )), Image.asset( getCircleImage('chat'), width: 60.sp, @@ -519,37 +587,55 @@ class InfoListView extends StatelessWidget { } ///普通图文喊话 - normalDynamicItem(Circle bean) { + normalDynamicItem(Lists bean) { Text descText = Text( - bean.intro, + bean.content!, style: TextStyle(color: Colors.white, fontSize: 14.sp), - maxLines: 2, + // maxLines: 2, ); - List urlList = [ - 'https://p3-passport.byteimg.com/img/user-avatar/eb429d4dbb3c246f580a6f7894f2b246~100x100.awebp', - 'https://p3-passport.byteimg.com/img/user-avatar/eb429d4dbb3c246f580a6f7894f2b246~100x100.awebp', - 'https://p3-passport.byteimg.com/img/user-avatar/eb429d4dbb3c246f580a6f7894f2b246~100x100.awebp' - ]; + double picHeight = 0.0; + + int type = 0; + if (bean.album != null) { + if (bean.album!.isNotEmpty) { + Album info = bean.album!.first!; + type = info.type!; + + if (info.type == 1) { + if (bean.album!.length > 3) { + picHeight = 218.sp; + } + } else { + picHeight = 109.sp; + } + } + } + List widgets = []; int index = 0; - urlList.forEach((element) { + bean!.chat!.users!.forEach((element) { widgets.add(Positioned( left: 12.sp * index, - child: circleWidget(element, width: 24), + child: GestureDetector( + onTap: () {}, + child: circleWidget(element.avatar!, width: 24), + ), )); index++; }); - widgets.add(Positioned( - left: 12.sp * urlList.length, - child: Image.asset( - getCircleImage('more'), - width: 24.sp, - ))); + if (widgets.isNotEmpty) { + widgets.add(Positioned( + left: 12.sp * bean.chat!.users!.length, + child: Image.asset( + getCircleImage('more'), + width: 24.sp, + ))); + } return Container( margin: EdgeInsets.only(top: 10.sp), width: Get.width, - height: 279.sp, + height: 130.sp + contentHeight(bean.content!) + picHeight, decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.fill, @@ -561,11 +647,23 @@ class InfoListView extends StatelessWidget { Positioned( right: 2.sp, top: 2.sp, - child: Image.asset(getCircleImage('location')), - height: 18.sp, + child: Stack( + alignment: Alignment.center, + children: [ + Image.asset( + getCircleImage('location'), + height: 20.sp, + ), + Text( + bean.user!.city ?? '外星', + style: TextStyle(color: Colors.white, fontSize: 12.sp), + ) + ], + ), + // height: 18.sp, ), Container( - height: 279.sp, + height: 130.sp + contentHeight(bean.content!) + picHeight, width: Get.width, padding: EdgeInsets.only(left: 12.sp, right: 12.sp), child: Column( @@ -577,118 +675,137 @@ class InfoListView extends StatelessWidget { children: [ GestureDetector( child: Stack( - alignment: Alignment.center, - children: [ - Image.asset( - getCircleImage('avatar_bg'), - width: 42.sp, - ), - ClipOval( - child: Image.network( - 'https://p3-passport.byteimg.com/img/user-avatar/eb429d4dbb3c246f580a6f7894f2b246~100x100.awebp', - width: 40.sp, - height: 40.sp, - fit: BoxFit.fill, - ), - ) - ], - )), + alignment: Alignment.center, + children: [ + Image.asset( + getCircleImage('avatar_bg'), + width: 42.sp, + ), + ClipOval( + child: Image.network( + bean.user!.avatar!, + width: 40.sp, + height: 40.sp, + fit: BoxFit.fill, + ), + ) + ], + )), Expanded( child: Container( - padding: EdgeInsets.only(left: 8.sp, top: 12.sp), - alignment: Alignment.centerLeft, - height: 72.sp, - child: Column( + padding: EdgeInsets.only(left: 8.sp, top: 12.sp), + alignment: Alignment.centerLeft, + height: 72.sp, + child: Column( + children: [ + Row( children: [ - Row( - children: [ - Text( - '圈子名称', - style: TextStyle( - color: Colors.white, - fontSize: 18.sp, - fontWeight: FontWeight.w600), - ), - SizedBox( - width: 8.sp, - ), - Image.asset( - getCircleImage('vip'), - width: 36.sp, - ) - ], + Text( + bean.user!.nickname! ?? '', + style: TextStyle( + color: Colors.white, + fontSize: 18.sp, + fontWeight: FontWeight.w600), ), SizedBox( - height: 4.sp, - ), - Row( - children: [ - Container( - alignment: Alignment.center, - height: 18.sp, - padding: - EdgeInsets.only(left: 6.sp, right: 6.sp), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(9.sp), - gradient: LinearGradient( - begin: Alignment(0.25, 0.5), - end: Alignment(0.75, 0.5), - colors: [ - Color(0xff8DFFF8), - Color(0xffB5D3FF) - ])), - child: Text( - '男.33.DOM.异性恋', - style: TextStyle( - color: Colors.black, - fontSize: 12.sp, - ), - ), - ) - ], + width: 8.sp, ), + bean.user!.vip! == 0 + ? Container() + : Image.asset( + getCircleImage('vip'), + width: 36.sp, + ) ], ), - )), + SizedBox( + height: 4.sp, + ), + Row( + children: [ + Container( + alignment: Alignment.center, + height: 18.sp, + padding: + EdgeInsets.only(left: 6.sp, right: 6.sp), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(9.sp), + gradient: const LinearGradient( + begin: Alignment(0.25, 0.5), + end: Alignment(0.75, 0.5), + colors: [ + Color(0xff8DFFF8), + Color(0xffB5D3FF) + ])), + child: Text( + '${genderList[bean.user!.gender!]}.${'33'}.${roleList[bean.user!.role!]}.${orientationList[bean.user!.orientation!]}', + style: TextStyle( + color: Colors.black, + fontSize: 12.sp, + ), + ), + ) + ], + ), + ], + ), + )), ], ), ), Container( + alignment: Alignment.topLeft, // margin: EdgeInsets.only(top: 4.sp), child: descText, ), - Container( - height: 100.sp, - margin: EdgeInsets.only(top: 5.sp), - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, //横轴三个子widget - crossAxisSpacing: 8.sp, - childAspectRatio: 1.0 //宽高比为1时,子widget - ), - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(6.sp), - child: Image.network( - 'https://pic1.zhimg.com/v2-3be05963f5f3753a8cb75b6692154d4a_720w.jpg?source=172ae18b', - fit: BoxFit.fill, - ), - ), - ClipRRect( - borderRadius: BorderRadius.circular(6.sp), - child: Image.network( - 'https://pic1.zhimg.com/v2-3be05963f5f3753a8cb75b6692154d4a_720w.jpg?source=172ae18b', - fit: BoxFit.fill), - ), - ClipRRect( - borderRadius: BorderRadius.circular(6.sp), - child: Image.network( - 'https://pic1.zhimg.com/v2-3be05963f5f3753a8cb75b6692154d4a_720w.jpg?source=172ae18b', - fit: BoxFit.fill), - ), - ]), - ), + picHeight > 0 + ? Container( + height: picHeight, + margin: EdgeInsets.only(top: 5.sp), + child: GridView.builder( + itemCount: bean.album!.length, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, //横轴三个子widget + crossAxisSpacing: 8.sp, + mainAxisSpacing: 8.sp, + childAspectRatio: 1.0 //宽高比为1时,子widget + ), + itemBuilder: (contentxt, currentIndex) { + Album album = bean.album![currentIndex]; + if (album.type == 2) { + return ClipRRect( + borderRadius: BorderRadius.circular(6.sp), + child: VideoItemWidget(album.url!)); + } + return GestureDetector( + onTap: () { + var imgList = []; + for (var element in bean.album!) { + imgList.add(element.url!); + } + Get.toNamed(AppRoutes.Swiper, arguments: { + 'imaglist': imgList, + 'index': currentIndex + }); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(6.sp), + child: CachedNetworkImage( + imageUrl: album.url!, + placeholder: (context, url) => + CircularProgressIndicator( + color: Color(0xFF07FAFB), + ), + errorWidget: (context, url, error) => + Icon(Icons.error), + fit: BoxFit.fill, + ), + ), + ); + })) + : Container(), Container( height: 34.sp, padding: EdgeInsets.only(left: 5.sp, right: 10.sp), @@ -701,19 +818,29 @@ class InfoListView extends StatelessWidget { colors: [Color(0xff261240), Color(0xff122D40)])), child: Row( children: [ - Container( - width: 24 + 12.sp * widgets.length - 12.sp, - height: 24.sp, - child: Stack(children: widgets), - ), + widgets.isNotEmpty + ? Container( + width: 24 + 12.sp * widgets.length - 12.sp, + height: 24.sp, + child: Stack(children: widgets), + ) + : Container(), SizedBox( width: 4.sp, ), - Expanded( - child: Text( - '1位圈友已私聊', - style: TextStyle(color: Colors.white, fontSize: 12.sp), - )), + widgets.isNotEmpty + ? Expanded( + child: Text( + '${bean.chat!.count!}位圈友已私聊', + style: TextStyle( + color: Colors.white, fontSize: 12.sp), + )) + : Expanded( + child: Text( + '赶紧成为第一位私聊ta的圈友吧', + style: TextStyle( + color: Colors.white, fontSize: 12.sp), + )), Image.asset( getCircleImage('chat'), width: 60.sp, @@ -729,29 +856,27 @@ class InfoListView extends StatelessWidget { ); } - circleWidget(String url, {double width = 30}) { return GestureDetector( child: Stack( - alignment: Alignment.center, - children: [ - Image.asset( - getCircleImage('avatar_bg'), - width: width.sp, - ), - ClipOval( - child: Image.network( - url, - width: (width - 1).sp, - height: (width - 1).sp, - fit: BoxFit.fill, - ), - ) - ], - )); + alignment: Alignment.center, + children: [ + Image.asset( + getCircleImage('avatar_bg'), + width: width.sp, + ), + ClipOval( + child: Image.network( + url, + width: (width - 1).sp, + height: (width - 1).sp, + fit: BoxFit.fill, + ), + ) + ], + )); } - void _showOutCircleDialog( BuildContext context, CircleLogic controller, Circle bean) { showDialog( @@ -795,7 +920,7 @@ class InfoListView extends StatelessWidget { child: Text( "提示", style: - TextStyle(color: Colors.white, fontSize: 16.sp), + TextStyle(color: Colors.white, fontSize: 16.sp), ), ), Container( @@ -889,4 +1014,9 @@ class InfoListView extends StatelessWidget { }, ); } -} \ No newline at end of file + + double contentHeight(String content) { + return calculateTextHeight( + content, 14.sp, FontWeight.w300, Get.width - 64.sp, 100); + } +} diff --git a/circle_app/lib/app/circle/widgets/list_logic.dart b/circle_app/lib/app/circle/widgets/list_logic.dart new file mode 100644 index 0000000..b06b97e --- /dev/null +++ b/circle_app/lib/app/circle/widgets/list_logic.dart @@ -0,0 +1,64 @@ +import 'package:circle_app/app/circle/state.dart'; +import 'package:circle_app/network/dio_manager.dart'; +import 'package:circle_app/util/util.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class ListLogic extends GetxController { + int callOutPage = 1; + + bool isLoad = true; + bool callOutMore = true; + var _circleId = ''; + List lists = []; + final ScrollController scrollController = ScrollController(); + + loadCallOutListData(String circleId) async { + _circleId = circleId; + var data = await DioManager.instance.get( + url: "/up-service/interest/$circleId/callouts", + params: {"page": callOutPage, "page_size": "20"}); + if (data["code"] == 200) { + List dataList = data["data"]["lists"]; + if (callOutPage == 1) { + if (lists.isNotEmpty) { + lists.clear(); + } + } + if (dataList.isNotEmpty) { + callOutMore = true; + for (var element in dataList) { + lists.add(Lists.fromJson(element)); + } + callOutPage++; + } else { + callOutMore = false; + } + update(); + } else { + showToast(data["msg"]); + } + } + + void refreshData() { + callOutPage = 1; + callOutMore = true; + loadCallOutListData(_circleId); + + } + + void loadMore() { + loadCallOutListData(_circleId); + } + + @override + void onInit() { + // TODO: implement onInit + super.onInit(); + scrollController.addListener(() { + if (scrollController.position.pixels == scrollController.position.maxScrollExtent) { + loadMore(); + } + }); + } +} \ No newline at end of file diff --git a/circle_app/lib/app/circle/widgets/play_video_view.dart b/circle_app/lib/app/circle/widgets/play_video_view.dart new file mode 100644 index 0000000..6f4f19b --- /dev/null +++ b/circle_app/lib/app/circle/widgets/play_video_view.dart @@ -0,0 +1,112 @@ +import 'package:circle_app/util/util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:video_player/video_player.dart'; + +class PlayVideoView extends StatefulWidget { + String url; + PlayVideoView(this.url, {Key? key}) : super(key: key); + @override + State createState() => _PlayVideoViewState(); +} + +class _PlayVideoViewState extends State { + VideoPlayerController? videoPlayerController; + + @override + void initState() { + super.initState(); + videoPlayerController = VideoPlayerController.network(widget.url) + ..initialize().then((_) { + // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. + setState(() {}); + }); + + videoPlayerController?.addListener(() async { + // value.position == value.duration + if (videoPlayerController?.value.position == + videoPlayerController?.value.duration) { + // videoPlayerController?.pause(); + await videoPlayerController?.seekTo(Duration.zero); + setState(() {}); + } + }); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + videoPlayerController?.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + body: Stack( + children: [ + Container( + width: Get.width, + height: Get.height, + child: GestureDetector( + onTap: () { + if (videoPlayerController!.value.isPlaying) { + videoPlayerController!.pause(); + } else { + videoPlayerController!.play(); + } + setState(() {}); + }, + child: Stack( + alignment: Alignment.center, + children: [ + AspectRatio( + aspectRatio: videoPlayerController!.value.aspectRatio, + child: VideoPlayer(videoPlayerController!)), + Center( + child: videoPlayerController!.value.isPlaying + ? Container() + : Image( + image: AssetImage(getMineImage("icon_play")), + width: 60.sp, + height: 60.sp, + ), + ), + ], + ), + ), + ), + Positioned( + bottom: 0, + child: SafeArea( + child: SizedBox( + width: Get.width, + height: 10.sp, + child: VideoProgressIndicator(videoPlayerController!, + allowScrubbing: true, + colors: const VideoProgressColors( + playedColor: Colors.white, + bufferedColor: Colors.white54, + backgroundColor: Colors.black)), + ), + )), + Positioned( + left: 15.sp, + top: Get.statusBarHeight, + child: GestureDetector( + onTap: () { + Get.back(); + }, + child: Image.asset( + 'assets/images/navigator/back.png', + width: 36.sp, + height: 36.sp, + )), + ) + ], + ), + ); + } +} diff --git a/circle_app/lib/app/circle/widgets/video_item.dart b/circle_app/lib/app/circle/widgets/video_item.dart new file mode 100644 index 0000000..48247ac --- /dev/null +++ b/circle_app/lib/app/circle/widgets/video_item.dart @@ -0,0 +1,62 @@ + +import 'package:circle_app/app/circle/widgets/play_video_view.dart'; +import 'package:circle_app/util/util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:video_player/video_player.dart'; + +class VideoItemWidget extends StatefulWidget { + String url; + VideoItemWidget(this.url,{Key? key}) : super(key: key); + @override + State createState() => _VideoItemWidgetState(); +} + +class _VideoItemWidgetState extends State { + VideoPlayerController? videoPlayerController; + + @override + void initState() { + super.initState(); + videoPlayerController = VideoPlayerController.network( + widget.url) + ..initialize().then((_) { + // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed. + setState(() {}); + }); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + videoPlayerController?.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Get.to(PlayVideoView(widget.url)); + }, + child: AspectRatio( + aspectRatio: videoPlayerController!.value.aspectRatio, + child: Stack( + children: [ + VideoPlayer(videoPlayerController!), + Center( + child: videoPlayerController!.value.isPlaying + ? Container() + : Image( + image: AssetImage(getMineImage("icon_play")), + width: 30.sp, + height: 30.sp, + ), + ), + ], + ), + ), + ); + } +} diff --git a/circle_app/lib/util/util.dart b/circle_app/lib/util/util.dart index d8a4d52..9f92f40 100644 --- a/circle_app/lib/util/util.dart +++ b/circle_app/lib/util/util.dart @@ -2,11 +2,11 @@ import 'dart:ui'; import 'package:circle_app/common/values/values.dart'; import 'package:circle_app/router/app_routers.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:get/get.dart'; -import 'package:get/get_state_manager/get_state_manager.dart'; + class Util { @@ -59,6 +59,16 @@ showToast(String msg) { ); } +loaddingWidget(bool isMore) { + return Container( + alignment: Alignment.center, + child: isMore ? const CircularProgressIndicator(color: Color(0xFF07FAFB),) : Container( + margin:EdgeInsets.only(top: 4.sp,bottom: 4.sp), + child: Text('--到底了--',style: TextStyle(color: Colors.white,fontSize: 13.sp), + ),), + ); +} + ///value: 文本内容;fontSize : 文字的大小;fontWeight:文字权重;maxWidth:文本框的最大宽度;maxLines:文本支持最大多少行 double calculateTextHeight(String value, fontSize, FontWeight fontWeight, double maxWidth, int maxLines) { diff --git a/circle_app/pubspec.yaml b/circle_app/pubspec.yaml index 9fc502f..e6a9f75 100644 --- a/circle_app/pubspec.yaml +++ b/circle_app/pubspec.yaml @@ -76,6 +76,8 @@ dependencies: package_info_plus: ^1.4.3+1 #视频播放器 video_player: ^2.6.1 + #图片缓存 + cached_network_image: ^3.2.3