diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d..2da766c 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/img_start.png b/android/app/src/main/res/mipmap-xxhdpi/img_start.png index 47bc3e2..1727e5a 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/img_start.png and b/android/app/src/main/res/mipmap-xxhdpi/img_start.png differ diff --git a/assets/images/img_start.png b/assets/images/img_start.png index 47bc3e2..1727e5a 100644 Binary files a/assets/images/img_start.png and b/assets/images/img_start.png differ diff --git a/lib/tools/chat/chat_page.dart b/lib/tools/chat/chat_page.dart index 2c9bd17..0b66286 100644 --- a/lib/tools/chat/chat_page.dart +++ b/lib/tools/chat/chat_page.dart @@ -4,6 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import '../../beans/character_info_bean.dart'; import '../../beans/chat_info_bean.dart'; @@ -52,12 +53,15 @@ class _ChatPageState extends State { int delIndex = 0; + ///聊天列表滑动到底部 void _scrollToBottom() { - _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - curve: Curves.easeOut, - duration: const Duration(milliseconds: 300), - ); + Future.delayed(Duration(milliseconds: 300), () { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + curve: Curves.easeOut, + duration: const Duration(milliseconds: 300), + ); + }); } @override @@ -77,9 +81,7 @@ class _ChatPageState extends State { chatList.addAll(newData['data']); EasyLoading.dismiss(); setState(() {}); - Future.delayed(Duration(milliseconds: 100), () { - _scrollToBottom(); - }); + _scrollToBottom(); break; case "sendMessage": sendMessageBean = newData['data']; @@ -122,6 +124,21 @@ class _ChatPageState extends State { } }); + // 创建键盘可见性监测器 + final keyboardVisibilityController = KeyboardVisibilityController(); + // 订阅键盘可见性变化 + keyboardVisibilityController.onChange.listen((bool visible) { + // 在这里处理键盘可见性变化的逻辑 + if (visible) { + // 键盘弹出时的处理逻辑 + print('Keyboard is visible'); + _scrollToBottom(); + } else { + // 键盘隐藏时的处理逻辑 + print('Keyboard is hidden'); + } + }); + loadData(); } @@ -155,7 +172,9 @@ class _ChatPageState extends State { children: [ characterInfoBean != null && characterInfoBean?.bgUrl != null ? CachedNetworkImage( - fit: BoxFit.fitHeight, + fit: BoxFit.cover, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, imageUrl: "${characterInfoBean?.bgUrl}", errorWidget: (context, url, error) => const Icon(Icons.error), ) diff --git a/lib/tools/find/multiplex_page.dart b/lib/tools/find/multiplex_page.dart index b328cae..1259d52 100644 --- a/lib/tools/find/multiplex_page.dart +++ b/lib/tools/find/multiplex_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import '../../beans/category_info_list_bean.dart'; +import '../chat/chat_page.dart'; import 'find_model.dart'; class MultiplexPage extends StatefulWidget { @@ -51,6 +52,16 @@ class _MultiplexPageState extends State { super.dispose(); } + goChatPage(String id) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatPage( + characterId: id, + )), + ); + } + @override Widget build(BuildContext context) { return Container( @@ -64,69 +75,75 @@ class _MultiplexPageState extends State { } _item(index, CategoryInfoListBean data) { - return Container( - height: 130, - width: 50, - margin: EdgeInsets.only(bottom: 23), - decoration: BoxDecoration( - color: Color(0xFF202021), - borderRadius: BorderRadius.all(Radius.circular(7)), - border: Border.all(color: Color(0xFF7E7E7E), width: 0.3), - ), - child: Stack( - alignment: Alignment.center, - children: [ - Positioned( - left: 7, - child: CachedNetworkImage( - width: 83, - height: 115, - imageUrl: data.iconImage!, - errorWidget: (context, url, error) => const Icon(Icons.error), + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + goChatPage(data.id.toString()); + }, + child: Container( + height: 130, + width: 50, + margin: EdgeInsets.only(bottom: 23), + decoration: BoxDecoration( + color: Color(0xFF202021), + borderRadius: BorderRadius.all(Radius.circular(7)), + border: Border.all(color: Color(0xFF7E7E7E), width: 0.3), + ), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 7, + child: CachedNetworkImage( + width: 83, + height: 115, + imageUrl: data.iconImage!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), ), - ), - Positioned( - left: 101, - top: 19, - child: Text( - '${data.name}', - style: TextStyle(color: Colors.white, fontSize: 14), - )), - Positioned( - left: 101, - top: 45, - child: SizedBox( - width: 200, - height: 16, - child: ListView.builder( - itemCount: data.label?.length, - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context, int index) { - return Container( - alignment: Alignment.center, - padding: EdgeInsets.symmetric(horizontal: 11), - margin: EdgeInsets.only(right: 5), - decoration: BoxDecoration(border: Border.all(color: Color(0xFFFF9000)), borderRadius: BorderRadius.all(Radius.circular(7))), - child: Text( - '${data.label?[index].name}', - style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), - ), - ); - }), - )), - Positioned( - left: 102, - top: 70, - child: Container( - width: 200, + Positioned( + left: 101, + top: 19, child: Text( - maxLines: 3, - '${data.biography}', - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Colors.white, fontSize: 10), - ), - )) - ], + '${data.name}', + style: TextStyle(color: Colors.white, fontSize: 14), + )), + Positioned( + left: 101, + top: 45, + child: SizedBox( + width: 200, + height: 16, + child: ListView.builder( + itemCount: data.label?.length, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 11), + margin: EdgeInsets.only(right: 5), + decoration: BoxDecoration(border: Border.all(color: Color(0xFFFF9000)), borderRadius: BorderRadius.all(Radius.circular(7))), + child: Text( + '${data.label?[index].name}', + style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), + ), + ); + }), + )), + Positioned( + left: 102, + top: 70, + child: Container( + width: 200, + child: Text( + maxLines: 3, + '${data.biography}', + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Colors.white, fontSize: 10), + ), + )) + ], + ), ), ); } diff --git a/lib/tools/find/recommend_page.dart b/lib/tools/find/recommend_page.dart index 581fdaf..42131b4 100644 --- a/lib/tools/find/recommend_page.dart +++ b/lib/tools/find/recommend_page.dart @@ -8,6 +8,7 @@ import 'package:flutter_easyloading/flutter_easyloading.dart'; import '../../beans/find_banner_bean.dart'; import '../../beans/recommend_bean.dart'; import '../../custom/custom_swiper_pagination.dart'; +import '../chat/chat_page.dart'; import 'find_model.dart'; ///推荐 @@ -18,7 +19,7 @@ class RecommendPage extends StatefulWidget { State createState() => _RecommendPageState(); } -class _RecommendPageState extends State { +class _RecommendPageState extends State with AutomaticKeepAliveClientMixin { late StreamSubscription subscription; final FindModel _viewmodel = FindModel(); @@ -74,6 +75,16 @@ class _RecommendPageState extends State { super.dispose(); } + goChatPage(String id) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatPage( + characterId: id, + )), + ); + } + @override Widget build(BuildContext context) { return Stack( @@ -92,10 +103,15 @@ class _RecommendPageState extends State { return Container( key: ValueKey(index), padding: EdgeInsets.only(bottom: 30), - child: CachedNetworkImage( - fit: BoxFit.fill, - imageUrl: bannerList[index].imageUrl!, - errorWidget: (context, url, error) => const Icon(Icons.error), + child: GestureDetector( + onTap: () { + goChatPage(bannerList[index].actionId.toString()); + }, + child: CachedNetworkImage( + fit: BoxFit.fill, + imageUrl: bannerList[index].imageUrl!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), ), ); }, @@ -115,19 +131,35 @@ class _RecommendPageState extends State { style: TextStyle(color: Colors.white, fontSize: 16), ), ), - Container( - margin: EdgeInsets.only(top: 21), - height: 360, - child: ListView.builder( - itemCount: recommendList.length, - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context, int index) { - return _item(index, recommendList[index]); - }), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Container( + // width: 500, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(top: 21), + padding: EdgeInsets.only(left: 16), + height: 330, + child: GridView.count( + shrinkWrap: true, + //水平子Widget之间间距 + crossAxisSpacing: 12.0, + //垂直子Widget之间间距 + mainAxisSpacing: 9.0, + //GridView内边距 + padding: EdgeInsets.zero, + //一行的Widget数量 + crossAxisCount: 2, + //子Widget宽高比例 + childAspectRatio: 1.4, + //子Widget列表 + children: _item(recommendList), + physics: NeverScrollableScrollPhysics(), + //类似 cellForRow 函数 + scrollDirection: Axis.horizontal)), ), Container( alignment: Alignment.centerLeft, - margin: EdgeInsets.only(left: 16), + margin: EdgeInsets.only(left: 16, top: 34), child: Text( '热门小说角色', style: TextStyle(color: Colors.white, fontSize: 16), @@ -150,48 +182,17 @@ class _RecommendPageState extends State { ], ); } -} -_item(int index, RecommendBean data) { - return Container( - margin: EdgeInsets.only(right: 9, left: index == 0 ? 16 : 0), - child: Column( - children: [ - Stack( - children: [ - ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(15)), - child: CachedNetworkImage( - width: 113, - height: 159, - fit: BoxFit.fill, - imageUrl: data.imageUrl!, - errorWidget: (context, url, error) => const Icon(Icons.error), - ), - ), - Positioned( - left: 7, - bottom: 21, - child: Text( - '${data.name}', - style: TextStyle(color: Colors.white, fontSize: 12), - )), - Positioned( - left: 7, - bottom: 9, - child: Container( - width: 105, - child: Text( - '${data.biography}', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Color(0xFFC2C2C2), fontSize: 9), - ), - )), - ], - ), - Container( - margin: EdgeInsets.only(top: 12), + _item(List list) { + return list.map((res) { + int index = recommendList.indexOf(res); + return GestureDetector( + onTap: () { + goChatPage(res.id.toString()); + }, + child: Container( + alignment: Alignment.center, + // margin: EdgeInsets.only(right: 9, left: 16), child: Stack( children: [ ClipRRect( @@ -200,7 +201,7 @@ _item(int index, RecommendBean data) { width: 113, height: 159, fit: BoxFit.fill, - imageUrl: data.imageUrl!, + imageUrl: res.imageUrl!, errorWidget: (context, url, error) => const Icon(Icons.error), ), ), @@ -208,7 +209,7 @@ _item(int index, RecommendBean data) { left: 7, bottom: 21, child: Text( - '${data.name}', + '${res.name}', style: TextStyle(color: Colors.white, fontSize: 12), )), Positioned( @@ -217,7 +218,7 @@ _item(int index, RecommendBean data) { child: Container( width: 105, child: Text( - '${data.biography}', + '${res.biography}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: Color(0xFFC2C2C2), fontSize: 9), @@ -225,80 +226,90 @@ _item(int index, RecommendBean data) { )), ], ), - ) - ], - ), - ); -} - -_item2(index, RecommendBean data) { - return Container( - height: 130, - width: 50, - margin: EdgeInsets.only(bottom: 20), - decoration: BoxDecoration( - color: Color(0xFF202021), - borderRadius: BorderRadius.all(Radius.circular(7)), - border: Border.all(color: Color(0xFF7E7E7E), width: 0.3), - ), - child: Stack( - alignment: Alignment.center, - children: [ - Positioned( - left: 7, - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(15)), - child: CachedNetworkImage( - width: 83, - height: 115, - fit: BoxFit.fill, - imageUrl: data.imageUrl!, - errorWidget: (context, url, error) => const Icon(Icons.error), - ), - ), ), - Positioned( - left: 101, - top: 19, - child: Text( - '${data.name}', - style: TextStyle(color: Colors.white, fontSize: 14), - )), - Positioned( - left: 101, - top: 45, - child: SizedBox( - width: 200, - height: 16, - child: ListView.builder( - itemCount: data.label?.length, - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext context, int index) { - return Container( - alignment: Alignment.center, - padding: EdgeInsets.symmetric(horizontal: 11), - margin: EdgeInsets.only(right: 5), - decoration: BoxDecoration(border: Border.all(color: Color(0xFFFF9000)), borderRadius: BorderRadius.all(Radius.circular(7))), - child: Text( - '${data.label?[index].name}', - style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), - ), - ); - }), - )), - Positioned( - left: 102, - top: 70, - child: Container( - width: 200, - child: Text( - maxLines: 3, - '${data.biography}', - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Colors.white, fontSize: 10), + ); + }).toList(); + } + + _item2(index, RecommendBean data) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + goChatPage(data.id.toString()); + }, + child: Container( + height: 130, + width: 50, + margin: EdgeInsets.only(bottom: 20), + decoration: BoxDecoration( + color: Color(0xFF202021), + borderRadius: BorderRadius.all(Radius.circular(7)), + border: Border.all(color: Color(0xFF7E7E7E), width: 0.3), + ), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + left: 7, + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(15)), + child: CachedNetworkImage( + width: 83, + height: 115, + fit: BoxFit.fill, + imageUrl: data.imageUrl!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), ), - )) - ], - ), - ); + ), + Positioned( + left: 101, + top: 19, + child: Text( + '${data.name}', + style: TextStyle(color: Colors.white, fontSize: 14), + )), + Positioned( + left: 101, + top: 45, + child: SizedBox( + width: 200, + height: 16, + child: ListView.builder( + itemCount: data.label?.length, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int index) { + return Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 11), + margin: EdgeInsets.only(right: 5), + decoration: BoxDecoration(border: Border.all(color: Color(0xFFFF9000)), borderRadius: BorderRadius.all(Radius.circular(7))), + child: Text( + '${data.label?[index].name}', + style: TextStyle(fontSize: 10, color: Color(0xFFFF9000)), + ), + ); + }), + )), + Positioned( + left: 102, + top: 70, + child: Container( + width: 200, + child: Text( + maxLines: 3, + '${data.biography}', + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Colors.white, fontSize: 10), + ), + )) + ], + ), + ), + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; } diff --git a/lib/tools/home/home_chat_page.dart b/lib/tools/home/home_chat_page.dart index 6c2264b..057a1eb 100644 --- a/lib/tools/home/home_chat_page.dart +++ b/lib/tools/home/home_chat_page.dart @@ -4,10 +4,12 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import '../../beans/character_info_bean.dart'; import '../../beans/chat_info_bean.dart'; import '../../beans/send_message_bean.dart'; +import '../../network/NetworkConfig.dart'; import '../chat/chat_info_page.dart'; import '../chat/chat_model.dart'; @@ -20,7 +22,7 @@ class HomeChatPage extends StatefulWidget { State createState() => _HomeChatPageState(); } -class _HomeChatPageState extends State with AutomaticKeepAliveClientMixin { +class _HomeChatPageState extends State with AutomaticKeepAliveClientMixin, WidgetsBindingObserver { final ScrollController _scrollController = ScrollController(); final TextEditingController _chatController = TextEditingController(); @@ -69,9 +71,7 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie case "getChatInfo": chatList.addAll(newData['data']); setState(() {}); - Future.delayed(Duration(milliseconds: 100), () { - _scrollToBottom(); - }); + _scrollToBottom(); EasyLoading.dismiss(); break; case "sendMessage": @@ -115,6 +115,21 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie } }); + // 创建键盘可见性监测器 + final keyboardVisibilityController = KeyboardVisibilityController(); + // 订阅键盘可见性变化 + keyboardVisibilityController.onChange.listen((bool visible) { + // 在这里处理键盘可见性变化的逻辑 + if (visible) { + // 键盘弹出时的处理逻辑 + print('Keyboard is visible'); + _scrollToBottom(); + } else { + // 键盘隐藏时的处理逻辑 + print('Keyboard is hidden'); + } + }); + loadData(); } @@ -131,12 +146,15 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie _viewmodel.delChatByIds(ids, widget.characterId); } + ///聊天列表滑动到底部 void _scrollToBottom() { - _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - curve: Curves.easeOut, - duration: const Duration(milliseconds: 300), - ); + Future.delayed(Duration(milliseconds: 300), () { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + curve: Curves.easeOut, + duration: const Duration(milliseconds: 300), + ); + }); } @override @@ -151,339 +169,364 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie @override Widget build(BuildContext context) { super.build(context); - return Stack( - children: [ - CachedNetworkImage( - fit: BoxFit.fitHeight, - imageUrl: characterInfoBean.bgUrl!, - errorWidget: (context, url, error) => const Icon(Icons.error), - ), - Positioned( - bottom: 0, - child: Container( - width: MediaQuery.of(context).size.width, - height: 311.67, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [Color(0x00000000), Color(0xFF0C0909)], // 三色渐变数组 - begin: Alignment.topCenter, // 渐变开始位置 - end: Alignment.bottomCenter, // 渐变结束位置 + return Scaffold( + backgroundColor: Color(0xFF121213), + body: Stack( + children: [ + CachedNetworkImage( + fit: BoxFit.cover, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + imageUrl: characterInfoBean.bgUrl!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), + Positioned( + bottom: 0, + child: Container( + width: MediaQuery.of(context).size.width, + height: 311.67, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Color(0x00000000), Color(0xFF0C0909)], // 三色渐变数组 + begin: Alignment.topCenter, // 渐变开始位置 + end: Alignment.bottomCenter, // 渐变结束位置 + ), ), - ), - )), - Container( - child: Column( - children: [ - ///title - Container( - width: double.infinity, - height: 30, - margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), - child: Stack( - alignment: Alignment.center, - children: [ - Positioned( - top: 0, - left: 105, - child: Text( - "+1", - style: TextStyle(color: Color(0xFFF14476), fontSize: 10), - )), - Positioned( - left: 0, - child: Container( - width: 101, - height: 30, - decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(14))), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ///AI头像 - GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ChatInfoPage( - data: characterInfoBean, - )), - ); - }, - child: Container( - margin: EdgeInsets.only(left: 2), - child: CachedNetworkImage( - width: 23, - imageUrl: characterInfoBean.icon!, - errorWidget: (context, url, error) => const Icon(Icons.error), + )), + Container( + child: Column( + children: [ + ///title + Container( + width: double.infinity, + height: 30, + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned( + top: 0, + left: 105, + child: Text( + "+1", + style: TextStyle(color: Color(0xFFF14476), fontSize: 10), + )), + Positioned( + left: 0, + child: Container( + width: 101, + height: 30, + decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(14))), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ///AI头像 + GestureDetector( + onTap: () { + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatInfoPage( + data: characterInfoBean, + )), + ); + }, + child: Container( + margin: EdgeInsets.only(left: 2), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: CachedNetworkImage( + width: 23, + height: 23, + imageUrl: characterInfoBean.icon!, + errorWidget: (context, url, error) => const Icon(Icons.error), + ), + ), ), ), - ), - ///AI名 - Container( - margin: EdgeInsets.only(left: 5), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 35, - child: Text( - characterInfoBean.characterName!, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 10, color: Colors.white), - ), - ), - Text( - '${characterInfoBean.lookCount} 聊过', - style: TextStyle(fontSize: 7, color: Colors.white), - ), - ], - ), - ), - - ///心动值 - Container( - margin: EdgeInsets.only(left: 6, right: 6), - child: GestureDetector( - onTap: () { - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => ChatPage( - // characterId: characterInfoBean.characterId.toString(), - // )), - // ); - }, - child: Stack( - alignment: Alignment.center, + ///AI名 + Container( + margin: EdgeInsets.only(left: 5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - Image( - width: 24, - height: 21, - image: AssetImage('assets/images/ic_beckoning.png'), + SizedBox( + width: 35, + child: Text( + characterInfoBean.characterName!, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 10, color: Colors.white), + ), ), Text( - characterInfoBean.intimacy.toString(), - style: TextStyle(fontSize: 8, color: Colors.white), - ) + '${characterInfoBean.lookCount} 聊过', + style: TextStyle(fontSize: 7, color: Colors.white), + ), ], ), ), - ), - ], - ), - ), - ), - ///关注 - Positioned( - right: 0, - child: GestureDetector( - onTap: () { - isHalf = !isHalf; - setState(() {}); - }, - child: Container( - width: 50, - height: 24, - alignment: Alignment.center, - decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(12))), - child: Text( - '+ 关注', - style: TextStyle(fontSize: 12, color: Colors.white), + ///心动值 + Container( + margin: EdgeInsets.only(left: 6, right: 6), + child: GestureDetector( + onTap: () { + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) => ChatPage( + // characterId: characterInfoBean.characterId.toString(), + // )), + // ); + }, + child: Stack( + alignment: Alignment.center, + children: [ + Image( + width: 24, + height: 21, + image: AssetImage('assets/images/ic_beckoning.png'), + ), + Text( + characterInfoBean.intimacy.toString(), + style: TextStyle(fontSize: 8, color: Colors.white), + ) + ], + ), + ), + ), + ], ), ), ), - ), - ], - ), - ), - isHalf - ? Container( - height: MediaQuery.of(context).size.height / 3.5, - ) - : Container(), - ///聊天列表 - Expanded( - child: ListView.builder( - controller: _scrollController, - itemCount: chatList.length, - itemBuilder: (BuildContext context, int index) { - return _item(index); - })), - Container( - alignment: Alignment.centerLeft, - margin: EdgeInsets.only(left: 16, bottom: 3), - child: Image(width: 63, height: 18, image: AssetImage('assets/images/ic_memory.png')), - ), - - ///输入 功能 - Container( - width: MediaQuery.of(context).size.width, - margin: EdgeInsets.only(left: 16, right: 16, bottom: !isMore ? 60 : 0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ///输入框 - Expanded( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 5), - decoration: const BoxDecoration( - color: Color(0x33FFFFFF), - borderRadius: BorderRadius.all(Radius.circular(20)), + ///关注 + Positioned( + right: 0, + child: GestureDetector( + onTap: () { + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } + isHalf = !isHalf; + setState(() {}); + }, + child: Container( + width: 50, + height: 24, + alignment: Alignment.center, + decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(12))), + child: Text( + '+ 关注', + style: TextStyle(fontSize: 12, color: Colors.white), + ), + ), + ), ), - child: Row( - children: [ - Expanded( - child: Container( - padding: const EdgeInsets.only(left: 15), - child: TextField( - controller: _chatController, - onChanged: _textFieldChanged, - maxLines: null, - cursorColor: const Color(0xFFFF9000), - decoration: const InputDecoration( - border: InputBorder.none, - // 移除非聚焦状态下的边框 - enabledBorder: InputBorder.none, - // 移除获得焦点但未输入内容时的边框 - focusedBorder: InputBorder.none, - isCollapsed: true, - // 移除输入时的边框 - hintText: "打个招呼吧...", - hintStyle: TextStyle(color: Color(0xFFB6B6B6), fontSize: 13), + ], + ), + ), + isHalf + ? Container( + height: MediaQuery.of(context).size.height / 3.5, + ) + : Container(), + + ///聊天列表 + Expanded( + child: ListView.builder( + controller: _scrollController, + itemCount: chatList.length, + itemBuilder: (BuildContext context, int index) { + return _item(index); + })), + Container( + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, bottom: 3), + child: Image(width: 63, height: 18, image: AssetImage('assets/images/ic_memory.png')), + ), + + ///输入 功能 + Container( + width: MediaQuery.of(context).size.width, + margin: EdgeInsets.only(left: 16, right: 16, bottom: !isMore ? 60 : 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ///输入框 + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 5), + decoration: const BoxDecoration( + color: Color(0x33FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 15), + child: TextField( + controller: _chatController, + onChanged: _textFieldChanged, + maxLines: null, + cursorColor: const Color(0xFFFF9000), + decoration: const InputDecoration( + border: InputBorder.none, + // 移除非聚焦状态下的边框 + enabledBorder: InputBorder.none, + // 移除获得焦点但未输入内容时的边框 + focusedBorder: InputBorder.none, + isCollapsed: true, + // 移除输入时的边框 + hintText: "打个招呼吧...", + hintStyle: TextStyle(color: Color(0xFFB6B6B6), fontSize: 13), + ), + style: const TextStyle(color: Colors.white), ), - style: const TextStyle(color: Colors.white), ), ), - ), - ///发送 - Container( - margin: const EdgeInsets.only(right: 7), - child: text == "" - ? const Image( - width: 27, - image: AssetImage('assets/images/ic_send_n.png'), - ) - : GestureDetector( - onTap: () { - EasyLoading.show(status: 'loading...'); - chatList.add(ChatInfoBean(0, "user", text, "timestamp", "claudeType", 0, "userIcon")); - _viewmodel.sendMessage(widget.characterId, text); - _chatController.clear(); - text = ""; - Future.delayed(Duration(milliseconds: 200), () { - _scrollController.jumpTo(_scrollController.position.maxScrollExtent); - }); - setState(() {}); - }, - child: Image( + ///发送 + Container( + margin: const EdgeInsets.only(right: 7), + child: text == "" + ? const Image( width: 27, - image: AssetImage('assets/images/ic_send.png'), + image: AssetImage('assets/images/ic_send_n.png'), + ) + : GestureDetector( + onTap: () { + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } + EasyLoading.show(status: 'loading...'); + chatList.add(ChatInfoBean(0, "user", text, "timestamp", "claudeType", 0, "userIcon")); + _viewmodel.sendMessage(widget.characterId, text); + _chatController.clear(); + text = ""; + Future.delayed(Duration(milliseconds: 200), () { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + }); + setState(() {}); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_send.png'), + ), ), - ), - ) - ], - ), - )), + ) + ], + ), + )), - ///智能输入 - Container( - margin: const EdgeInsets.only(left: 14, right: 14), - child: GestureDetector( + ///智能输入 + Container( + margin: const EdgeInsets.only(left: 14, right: 14), + child: GestureDetector( + onTap: () { + Navigator.of(context).pushNamed('/LoginPage'); + }, + child: Image( + width: 27, + image: AssetImage('assets/images/ic_smart_chat.png'), + ), + ), + ), + + ///功能 更多 + GestureDetector( onTap: () { - Navigator.of(context).pushNamed('/LoginPage'); + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } + isMore = !isMore; + setState(() {}); }, child: Image( width: 27, - image: AssetImage('assets/images/ic_smart_chat.png'), + image: AssetImage('assets/images/ic_more.png'), ), ), - ), - - ///功能 更多 - GestureDetector( - onTap: () { - isMore = !isMore; - setState(() {}); - }, - child: Image( - width: 27, - image: AssetImage('assets/images/ic_more.png'), - ), - ), - ], + ], + ), ), - ), - isMore - ? Container( - height: 70, - alignment: Alignment.centerLeft, - margin: EdgeInsets.only(left: 16, right: 16, bottom: 60), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - margin: EdgeInsets.only(left: 23), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Image(width: 26, image: AssetImage('assets/images/ic_album.png')), - Container( - margin: EdgeInsets.only(top: 9), - child: Text( - '角色相册', - style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), - ), - ), - ], - ), - ), - Container( - margin: EdgeInsets.only(left: 23), - child: GestureDetector( - onTap: () { - EasyLoading.show(status: 'loading...'); - _viewmodel.delChat(widget.characterId); - }, + isMore + ? Container( + height: 70, + alignment: Alignment.centerLeft, + margin: EdgeInsets.only(left: 16, right: 16, bottom: 60), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + margin: EdgeInsets.only(left: 23), child: Column( mainAxisSize: MainAxisSize.min, children: [ - Image(width: 26, height: 26, image: AssetImage('assets/images/ic_restart.png')), + Image(width: 26, image: AssetImage('assets/images/ic_album.png')), Container( margin: EdgeInsets.only(top: 9), child: Text( - '重启对话', + '角色相册', style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), ), ), ], ), ), - ), - ], - ), - ) - : Container(), - ], + Container( + margin: EdgeInsets.only(left: 23), + child: GestureDetector( + onTap: () { + EasyLoading.show(status: 'loading...'); + _viewmodel.delChat(widget.characterId); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Image(width: 26, height: 26, image: AssetImage('assets/images/ic_restart.png')), + Container( + margin: EdgeInsets.only(top: 9), + child: Text( + '重启对话', + style: TextStyle(fontSize: 10, color: Color(0xFFA2A2A2)), + ), + ), + ], + ), + ), + ), + ], + ), + ) + : Container(), + ], + ), ), - ), - ///记忆提升中 - Positioned( - top: 65, - left: 16, - child: Image( - width: 47, - height: 12, - image: AssetImage('assets/images/ic_memory_p.png'), - )), - ], + ///记忆提升中 + Positioned( + top: (MediaQuery.of(context).padding.top) * 2 + 10, + left: 16, + child: Image( + width: 47, + height: 12, + image: AssetImage('assets/images/ic_memory_p.png'), + )), + ], + ), ); } diff --git a/lib/tools/home_page.dart b/lib/tools/home_page.dart index 6e827a9..3a91213 100644 --- a/lib/tools/home_page.dart +++ b/lib/tools/home_page.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:talk/network/NetworkConfig.dart'; import 'package:talk/tools/home/my_home_page.dart'; import 'package:talk/tools/me/me_page.dart'; import 'package:talk/tools/message/message_page.dart'; @@ -74,6 +75,11 @@ class _HomePageState extends State with SingleTickerProviderStateMixin ), GestureDetector( onTap: () { + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } + currentIndex = 1; _tabController.animateTo(1); setState(() {}); @@ -89,6 +95,10 @@ class _HomePageState extends State with SingleTickerProviderStateMixin ), GestureDetector( onTap: () { + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } currentIndex = 2; _tabController.animateTo(2); setState(() {}); @@ -100,6 +110,10 @@ class _HomePageState extends State with SingleTickerProviderStateMixin ), GestureDetector( onTap: () { + if (NetworkConfig.userId == "") { + Navigator.of(context).pushNamed('/LoginPage'); + return; + } currentIndex = 3; _tabController.animateTo(3); setState(() {}); diff --git a/lib/tools/login/login_page.dart b/lib/tools/login/login_page.dart index 00341d5..c6728ab 100644 --- a/lib/tools/login/login_page.dart +++ b/lib/tools/login/login_page.dart @@ -105,168 +105,169 @@ class _LoginPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Stack( - alignment: Alignment.center, - children: [ - Image(fit: BoxFit.fill, image: AssetImage('assets/images/login_bj.png')), - Positioned( - left: 22, - top: 28, - child: GestureDetector( - onTap: () { - Navigator.pop(context); - }, - child: Image( - height: 23, - image: AssetImage('assets/images/ic_left_arrow.png'), - ), - )), - Positioned( - left: 36, - top: 76, - child: Text( - '妙语', - style: TextStyle(color: Colors.white, fontSize: 33), - )), - Positioned( - left: 36, - top: 115, - child: Text( - '登陆 / 注册', - style: TextStyle(color: Color(0xFFD5D5D5), fontSize: 18), - )), - Positioned( - bottom: 35, - child: Container( - width: MediaQuery.of(context).size.width, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - GestureDetector( - onTap: () { - isCheck = !isCheck; - setState(() {}); - }, - child: Image(width: 16, image: isCheck ? AssetImage('assets/images/ic_ck_s.png') : AssetImage('assets/images/ic_ck.png')), - ), - Container( - margin: EdgeInsets.only(left: 10), - child: RichText( - text: TextSpan(children: [ - TextSpan(text: '我已阅读并同意', style: TextStyle(fontSize: 12, color: Color(0xFF5F5F5F))), - TextSpan(text: '用户协议', style: TextStyle(fontSize: 12, color: Color(0xFFFF9000))), - TextSpan(text: '和', style: TextStyle(fontSize: 12, color: Color(0xFF5F5F5F))), - TextSpan(text: '隐私协议', style: TextStyle(fontSize: 12, color: Color(0xFFFF9000))), - ]), + body: SingleChildScrollView( + child: Stack( + alignment: Alignment.center, + children: [ + Image(fit: BoxFit.cover, height: MediaQuery.of(context).size.height, image: AssetImage('assets/images/login_bj.png')), + Positioned( + left: 22, + top: MediaQuery.of(context).padding.top + 9, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Image( + height: 23, + image: AssetImage('assets/images/ic_left_arrow.png'), + ), + )), + Positioned( + left: 36, + top: 76, + child: Text( + '妙语', + style: TextStyle(color: Colors.white, fontSize: 33), + )), + Positioned( + left: 36, + top: 115, + child: Text( + '登陆 / 注册', + style: TextStyle(color: Color(0xFFD5D5D5), fontSize: 18), + )), + Positioned( + bottom: 35, + child: Container( + width: MediaQuery.of(context).size.width, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + isCheck = !isCheck; + setState(() {}); + }, + child: Image(width: 16, image: isCheck ? AssetImage('assets/images/ic_ck_s.png') : AssetImage('assets/images/ic_ck.png')), ), - ) - ], - ), - )), - Positioned( - bottom: 70, - child: GestureDetector( - onTap: () { - if (phoneText != "" && codeText != "") { - if (isCheck) { - EasyLoading.showToast("登录"); - _viewmodel.login(phoneText, codeText, 0, - "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJOaWNrTmFtZSI6IuaWsOeUqOaItyIsIlVzZXJJZCI6IjMiLCJleHAiOjE3MjEyMjQzNjksImlzcyI6Ikh1YW5NZW5nIiwiYXVkIjoiSHVhbk1lbmdBcHAifQ.gxf2y47qWuNtEf2xvGSG6ATEgaXOrTWbJpi_Bp6eU2k"); - } else { - EasyLoading.showToast("请选中协议"); + Container( + margin: EdgeInsets.only(left: 10), + child: RichText( + text: TextSpan(children: [ + TextSpan(text: '我已阅读并同意', style: TextStyle(fontSize: 12, color: Color(0xFF5F5F5F))), + TextSpan(text: '用户协议', style: TextStyle(fontSize: 12, color: Color(0xFFFF9000))), + TextSpan(text: '和', style: TextStyle(fontSize: 12, color: Color(0xFF5F5F5F))), + TextSpan(text: '隐私协议', style: TextStyle(fontSize: 12, color: Color(0xFFFF9000))), + ]), + ), + ) + ], + ), + )), + Positioned( + bottom: 70, + child: GestureDetector( + onTap: () { + if (phoneText != "" && codeText != "") { + if (isCheck) { + EasyLoading.showToast("登录"); + _viewmodel.login(phoneText, codeText, 1, ""); + } else { + EasyLoading.showToast("请选中协议"); + } } - } - }, + }, + child: Container( + height: 43, + width: 288, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Color(phoneText != "" && codeText != "" ? 0xFFFF9000 : 0xFFAC6D1C), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + child: Text( + '登录', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600), + ), + ), + )), + Positioned( + bottom: 125, child: Container( height: 43, width: 288, alignment: Alignment.center, decoration: BoxDecoration( - color: Color(phoneText != "" && codeText != "" ? 0xFFFF9000 : 0xFFAC6D1C), + color: Color(0x33FFFFFF), borderRadius: BorderRadius.all(Radius.circular(7)), ), - child: Text( - '登录', - style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600), - ), - ), - )), - Positioned( - bottom: 125, - child: Container( - height: 43, - width: 288, - alignment: Alignment.center, - decoration: BoxDecoration( - color: Color(0x33FFFFFF), - borderRadius: BorderRadius.all(Radius.circular(7)), - ), - child: Row( - children: [ - Expanded( - child: Container( - padding: EdgeInsets.only(left: 17, bottom: 6), - child: TextField( - controller: _codeController, - onChanged: _codeChanged, - cursorColor: Color(0xFFFF9000), - keyboardType: TextInputType.number, - decoration: InputDecoration( - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - hintText: "请输入验证码", - hintStyle: TextStyle(color: Color(0xFFB5B5B5), fontSize: 15), + child: Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.only(left: 17, bottom: 6), + child: TextField( + controller: _codeController, + onChanged: _codeChanged, + cursorColor: Color(0xFFFF9000), + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintText: "请输入验证码", + hintStyle: TextStyle(color: Color(0xFFB5B5B5), fontSize: 15), + ), + style: TextStyle(color: Colors.white), ), - style: TextStyle(color: Colors.white), ), ), - ), - GestureDetector( - onTap: () { - if (!_isCountingDown) { - getCode(); - } - }, - child: Container( - margin: EdgeInsets.only(right: 14), - child: Text( - !_isCountingDown ? "获取验证码" : "$_timeLeft后重新获取", - style: TextStyle(fontSize: 15, color: Color(0xFFB5B5B5)), + GestureDetector( + onTap: () { + if (!_isCountingDown) { + getCode(); + } + }, + child: Container( + margin: EdgeInsets.only(right: 14), + child: Text( + !_isCountingDown ? "获取验证码" : "$_timeLeft后重新获取", + style: TextStyle(fontSize: 15, color: Color(0xFFB5B5B5)), + ), ), ), - ), - ], - ), - )), - Positioned( - bottom: 178, - child: Container( - height: 43, - width: 288, - alignment: Alignment.center, - padding: EdgeInsets.only(left: 17, bottom: 6), - decoration: BoxDecoration( - color: Color(0x33FFFFFF), - borderRadius: BorderRadius.all(Radius.circular(7)), - ), - child: TextField( - controller: _phoneController, - onChanged: _phoneChanged, - cursorColor: Color(0xFFFF9000), - keyboardType: TextInputType.number, - decoration: InputDecoration( - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - hintText: "请输入手机号", - hintStyle: TextStyle(color: Color(0xFFB5B5B5), fontSize: 15), + ], ), - style: TextStyle(color: Colors.white), - ), - )), - ], + )), + Positioned( + bottom: 178, + child: Container( + height: 43, + width: 288, + alignment: Alignment.center, + padding: EdgeInsets.only(left: 17, bottom: 6), + decoration: BoxDecoration( + color: Color(0x33FFFFFF), + borderRadius: BorderRadius.all(Radius.circular(7)), + ), + child: TextField( + controller: _phoneController, + onChanged: _phoneChanged, + cursorColor: Color(0xFFFF9000), + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + hintText: "请输入手机号", + hintStyle: TextStyle(color: Color(0xFFB5B5B5), fontSize: 15), + ), + style: TextStyle(color: Colors.white), + ), + )), + ], + ), ), ); } diff --git a/lib/tools/start_page.dart b/lib/tools/start_page.dart index 4255d77..d3c00b0 100644 --- a/lib/tools/start_page.dart +++ b/lib/tools/start_page.dart @@ -61,9 +61,10 @@ class _StartPageState extends State { @override Widget build(BuildContext context) { return Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, child: Image( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + fit: BoxFit.cover, image: AssetImage('assets/images/img_start.png'), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 56e5791..001a31f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: expandable_text: ^2.3.0 card_swiper: ^3.0.1 flutter_slidable: ^3.1.0 + flutter_keyboard_visibility: ^6.0.0 dev_dependencies: flutter_test: