From 35822f42879b0c0180d6a87074b5f3fce66b17dc Mon Sep 17 00:00:00 2001 From: apple <> Date: Thu, 28 Jul 2022 18:03:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=99=BB=E5=BD=95=E9=A1=B5?= =?UTF-8?q?=EF=BC=8C=E8=AF=AD=E8=81=8A=E6=88=BF=E9=A1=B5=EF=BC=8Ctost?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=B0=81=E8=A3=85=EF=BC=8Cdio=E5=B0=81?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- images/createRoom/refresh.png | Bin 0 -> 1086 bytes ios/Podfile.lock | 10 + lib/Tab.dart | 1 + lib/main.dart | 8 +- lib/routers/Routes.dart | 40 +-- lib/utils/dioHttp.dart | 102 ++++++ lib/utils/showToast.dart | 98 ++++++ lib/views/createRoom/CreateRoom.dart | 315 +++++++++++++++++++ lib/views/createRoom/widget/CustomRadio.dart | 89 ++++++ lib/views/login/Login.dart | 177 +++++++++++ lib/views/palRoom/PalRoom.dart | 250 +++++++++------ lib/views/party/Party.dart | 2 +- pubspec.lock | 35 +++ pubspec.yaml | 5 + 14 files changed, 991 insertions(+), 141 deletions(-) create mode 100644 images/createRoom/refresh.png create mode 100644 lib/utils/dioHttp.dart create mode 100644 lib/utils/showToast.dart create mode 100644 lib/views/createRoom/CreateRoom.dart create mode 100644 lib/views/createRoom/widget/CustomRadio.dart create mode 100644 lib/views/login/Login.dart diff --git a/images/createRoom/refresh.png b/images/createRoom/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..4830680427f9743c2cc018996ed25dcbfd9e05a7 GIT binary patch literal 1086 zcmV-E1i|}>P)P001Hg1^@s6s#J0f00001b5ch_0Itp) z=>Px&_(?=TR7gv;mTQO`RTRhn=O*>Bixv@D>*Is(vLaa9+TA3JE@o!fd{9v~g34kM zDPk>R5sOqnds#s+ewB>AFeg$Rv}; z#!McK-fuJa-19rndoH1i6_3vj_GYf<*AXO< zNZ;V|(ebk4{{b>Row?bz?JsJU|HlwrPi+)J9Bl~AZsy_)(W*c5`P_UY68XDb=sd&a z0cfUgKeLEgmY$iIn6R4(w1Ak;34z`1tgP*GqEPTXGCDf+W8;~YiBBfYNoMS|`O$&Zd|G#Z@8HV{h5uR?Acqvo2F^gx3H~^5pBEyVvRSmvV`ECnw z1P#M{fe24|m9VZ`0Uz8l1X!OvFQIp)3*HJm4h0nc?nyVb^hX_ zeS(NCb#?rd%VpO`qtP=ZPYfgdArVIMdH;=($lx#DpwuLh{}Zo{PbQCCXWNBiL^#LM z!Hiw18hnv-{j)2D@Xx!77yA2YT_6xR;{}Sx4__FG44!V`@m?bAC`0&n%Sv8ChLL)d zNW5B#9pS5RICxX%W!qd*O4Bj&JZIOoaXU$AehlEwvN%sFO6bYnA!!Lv*!5-)8HV{C z5w0my?ZIxZ@n!}YMtTnsJ>y2_ea927l^xb!Fzy5I_tl%Fx%n zLZnP@2PjsB7)JVgBD$*l!yZwT;jRW!GlQf|#{k;yhU-nolPLgqxWX|-36{#Qmt`2{ z3=wX1Av+z&V*p-pmCh+jaD6X;R4R4R(vooUTDr@Ymw7WCI&}E*LZSF`MKQBIuByY; z%dHW3;6VEN(b3SHmm$u+-%rN?tYYSG6eT!Nx>lr2zXi~^>t3#}k1q4-dM6aj{JN%v z_IrVJJ+nm!dzP7=P}RU|C5WzneuWVEZ!292@LYet*w74Ai8nLdr>cSXyg-JL-jvPe zk8j;NvQ)k5r8MsWP#reRI4K0~3WtN4dPVg~Z3n8HTQN_hv>pfe}D* z`~4gvqR~#vnVG_>8klY--)j8Q!GoDwibZ}9;4$~%;Klqjm&=YhQ?Oge3i(80cAXHu zwE*uUqK&n_;*U<9`t^PM&Cj1Zwr$%c=MC0! zoh@WBm&@KAjYgN-0nRF1x}Ms#u<+YU(P*^T3BZy02LOt%tJ5CyN&o-=07*qoM6N<$ Ef_}~otpET3 literal 0 HcmV?d00001 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index ae820fd..2742d03 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,23 +4,31 @@ PODS: - Flutter - AgoraIrisRTC_iOS (3.6.2-fix.1) - Flutter (1.0.0) + - fluttertoast (0.0.2): + - Flutter + - Toast - "permission_handler (5.1.0+2)": - Flutter + - Toast (4.0.0) DEPENDENCIES: - agora_rtc_engine (from `.symlinks/plugins/agora_rtc_engine/ios`) - Flutter (from `Flutter`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - permission_handler (from `.symlinks/plugins/permission_handler/ios`) SPEC REPOS: trunk: - AgoraIrisRTC_iOS + - Toast EXTERNAL SOURCES: agora_rtc_engine: :path: ".symlinks/plugins/agora_rtc_engine/ios" Flutter: :path: Flutter + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" permission_handler: :path: ".symlinks/plugins/permission_handler/ios" @@ -28,7 +36,9 @@ SPEC CHECKSUMS: agora_rtc_engine: ffc530eff766a38272568ebaead9341d73f58856 AgoraIrisRTC_iOS: 9b11083bb79048ae4db7e2f56109cc51ac9e3834 Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58 permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 + Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 PODFILE CHECKSUM: bd5ecd90bb4d3dada07dd6c6725bb70ab42e9619 diff --git a/lib/Tab.dart b/lib/Tab.dart index bb2b127..66b776e 100644 --- a/lib/Tab.dart +++ b/lib/Tab.dart @@ -25,6 +25,7 @@ class _TabsState extends State { InformationPage(), MinePage() ]; + _TabsState(index) { this._currentIndex = index; } diff --git a/lib/main.dart b/lib/main.dart index 15c51c1..4315925 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,9 +16,13 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { return MaterialApp( + theme: ThemeData( + primarySwatch: Colors.pink, + splashColor: Colors.transparent, // 点击时的高亮效果设置为透明 + highlightColor: Colors.transparent, // 长按时的扩散效果设置为透明 + ), debugShowCheckedModeBanner: false, - initialRoute: '/', //默认跳转页面 - theme: ThemeData(primarySwatch: Colors.pink), + initialRoute: 'Login', // 默认跳转页面 onGenerateRoute: onGenerateRoute, ); } diff --git a/lib/routers/Routes.dart b/lib/routers/Routes.dart index baa3f84..503ccc9 100644 --- a/lib/routers/Routes.dart +++ b/lib/routers/Routes.dart @@ -1,46 +1,14 @@ import 'package:flutter/material.dart'; import '../Tab.dart'; -// import '../pages/routeParms/RouteParms.dart'; -// import '../pages/routeParmsDT/RouteParmsDT.dart'; - -// import '../pages/login/Login.dart'; -// import '../pages/login/RegisterFirsr.dart'; -// import '../pages/login/RegisterSecond.dart'; -// import '../pages/login/RegisterThird.dart'; -// import '../pages/appBarDemo/AppBarDemo.dart'; -// import '../pages/appBarDemo/AppBarController.dart'; -// import '../pages/button/Button.dart'; -// import '../pages/textField/TextField.dart'; -// import '../pages/textField/Checkbox.dart'; -// import '../pages/textField/Radio.dart'; -// import '../pages/textField/FormDemo.dart'; -// import '../pages/date/Date.dart'; -// import '../pages/dialog/Dialog.dart'; -// import '../pages/https/Https.dart'; -// import '../views/home/Home.dart'; import '../views/palRoom/PalRoom.dart'; +import '../views/createRoom/CreateRoom.dart'; +import '../views/login/Login.dart'; final routes = { '/': (context) => Tabs(), 'PalRoom': (context) => PalRoomPage(), - // '/home': (context) => HomePage(), - // '/routeParms': (context, {arguments}) => RouteParmsPage(arguments: arguments), - // '/RouteParmsDT': (context, {arguments}) => - // RouteParmsDTPage(arguments: arguments), - // '/login': (context) => LoginPage(), - // '/registerFirsr': (context) => RegisterFirsrPage(), - // '/registerSecond': (context) => RegisterSecondPage(), - // '/registerThird': (context) => RegisterThirdPage(), - // '/appBarDemo': (context) => AppBarDemoPage(), - // '/appBarController': (context) => AppBarControllerPage(), - // '/button': (context) => ButtonPage(), - // '/textField': (context) => TextFieldPage(), - // '/checkbox': (context) => CheckboxPage(), - // '/radio': (context) => RadioPage(), - // '/formDemo': (context) => FormDemoPage(), - // '/date': (context) => DatePage(), - // '/dialog': (context) => DialogPage(), - // '/https': (context) => HttpsPage(), + 'CreateRoom': (context) => CreateRoomPage(), + 'Login': (context) => LoginPage(), }; var onGenerateRoute = (RouteSettings settings) { diff --git a/lib/utils/dioHttp.dart b/lib/utils/dioHttp.dart new file mode 100644 index 0000000..e515515 --- /dev/null +++ b/lib/utils/dioHttp.dart @@ -0,0 +1,102 @@ +import 'package:dio/dio.dart'; + +var token = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ0ZXN0MSJ9.GV21nLXeLwZI7lB5Mp2lk9kEBJl4kJOPtw7YXwXINrE'; + +// Dio dio = new Dio(); +// dio.options = BaseOptions( +// baseUrl: 'http://101.35.117.69:9093/', +// connectTimeout: 5000, +// receiveTimeout: 5000, +// headers: {"Authorization": 'Bearer ${token}'}, +// contentType: Headers.formUrlEncodedContentType, +// ); + +// dio.Interceptors.add( +// InterceptorsWrapper( +// onRequest: (options, handler) {}, +// onResponse: (response, handler) {}, +// onError: (DioError e, handler) {}, +// ), +// ); + +class dioHttp { + static final options = BaseOptions( + baseUrl: 'http://101.35.117.69:9093/', + connectTimeout: 5000, + receiveTimeout: 5000, + headers: {"Authorization": 'Bearer ${token}'}, + ); + + Dio dio = new Dio(options); +} + +// Dio dio = new Dio( +// BaseOptions( +// baseUrl: 'http://101.35.117.69:9093/', +// connectTimeout: 5000, +// receiveTimeout: 5000, +// headers: {"Authorization": 'Bearer ${token}'}, +// contentType: Headers.formUrlEncodedContentType, +// ), +// Interceptors.add( +// InterceptorsWrapper( +// onRequest: (options, handler) {}, +// onResponse: (response, handler) {}, +// onError: (DioError e, handler) {}, +// ), +// ), +// ); + + + // //添加拦截器 + // dio.interceptors.add(InterceptorsWrapper( + // onRequest:(options, handler){ + // // Do something before request is sent + // return handler.next(options); //continue + // // 如果你想完成请求并返回一些自定义数据,你可以resolve一个Response对象 `handler.resolve(response)`。 + // // 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义response. + // // + // // 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象,如`handler.reject(error)`, + // // 这样请求将被中止并触发异常,上层catchError会被调用。 + // }, + // onResponse:(response,handler) { + // // Do something with response data + // return handler.next(response); // continue + // // 如果你想终止请求并触发一个错误,你可以 reject 一个`DioError`对象,如`handler.reject(error)`, + // // 这样请求将被中止并触发异常,上层catchError会被调用。 + // }, + // onError: (DioError e, handler) { + // // Do something with response error + // return handler.next(e);//continue + // // 如果你想完成请求并返回一些自定义数据,可以resolve 一个`Response`,如`handler.resolve(response)`。 + // // 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义response. + // } + // )); + + + +// // 发起一个 GET 请求: +// Response response; +// var dio = Dio(); +// response = await dio.get('/test?id=12&name=wendu'); +// print(response.data.toString()); +// // 上面的请求也可以这样做 +// response = await dio.get('/test', queryParameters: {'id': 12, 'name': 'wendu'}); +// print(response.data.toString()); + +// // 发起一个 POST 请求: +// response = await dio.post('/test', data: {'id': 12, 'name': 'wendu'}); + +// // 发起多个并发请求: +// response = await Future.wait([dio.post('/info'), dio.get('/token')]); + +// // 下载文件: +// response = await dio.download('https://www.google.com/', './xx.html'); + +// // 发送 FormData: +// var formData = FormData.fromMap({ +// 'name': 'wendux', +// 'age': 25, +// }); +// var response = await dio.post('/info', data: formData); \ No newline at end of file diff --git a/lib/utils/showToast.dart b/lib/utils/showToast.dart new file mode 100644 index 0000000..d41a492 --- /dev/null +++ b/lib/utils/showToast.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +// 普通气泡弹框 +// 使用:showToast("xxxxxxx"); +void showToast( + String text, { + gravity: ToastGravity.CENTER, + toastLength: Toast.LENGTH_SHORT, +}) { + Fluttertoast.showToast( + msg: text, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.CENTER, + timeInSecForIosWeb: 3, + backgroundColor: Colors.grey[600], // 灰色背景 + fontSize: 16.0, + ); +} + +// 一个加载中的动画,不传text时,默认显示文字Loading... +// 使用:showLoading(context, 'xxxxxxx'); +void showLoading(context, text) { + text = text ?? "Loading..."; + showDialog( + barrierDismissible: false, + context: context, + builder: (context) { + return Center( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(3.0), + boxShadow: [ + // 阴影 + BoxShadow( + color: Colors.black12, + //offset: Offset(2.0,2.0), + blurRadius: 10.0, + ) + ]), + padding: EdgeInsets.all(16), + margin: EdgeInsets.all(16), + constraints: BoxConstraints(minHeight: 120, minWidth: 180), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 30, + width: 30, + child: CircularProgressIndicator( + strokeWidth: 3, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 20.0), + child: Text( + text, + style: Theme.of(context).textTheme.bodyMedium, + ), + ), + ], + ), + ), + ); + }, + ); +} + +// 带有确定取消按钮的提示框,content是提示框的内容文字,confirmCallback是点击确定按钮的回调 +// 使用:showConfirmDialog(context, '确认xxxxxxx吗?', () {print('xxxxxxx');}); +void showConfirmDialog( + BuildContext context, String content, Function confirmCallback) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text("提示"), + content: Text(content), + actions: [ + FlatButton( + onPressed: () { + confirmCallback(); + Navigator.of(context).pop(); + }, + child: Text("确认"), + ), + FlatButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text("取消"), + ), + ], + ); + }); +} diff --git a/lib/views/createRoom/CreateRoom.dart b/lib/views/createRoom/CreateRoom.dart new file mode 100644 index 0000000..d1ae988 --- /dev/null +++ b/lib/views/createRoom/CreateRoom.dart @@ -0,0 +1,315 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class CreateRoomPage extends StatefulWidget { + CreateRoomPage({Key? key}) : super(key: key); + + @override + State createState() => _CreateRoomPageState(); +} + +class _CreateRoomPageState extends State { + final _formKey = GlobalKey(); + final _roomName = TextEditingController(); + bool flag = false; + + List patternList = [ + RadioModel( + true, + '普通', + ), + RadioModel( + false, + '相亲', + ), + RadioModel( + false, + '拍卖', + ), + RadioModel( + false, + 'KTV', + ), + ]; + + List tagList = [ + RadioModel( + true, + '交友', + ), + RadioModel( + false, + '音乐', + ), + RadioModel( + false, + '游戏', + ), + RadioModel( + false, + '连麦聊', + ), + ]; + + Widget _buttonstyle(theme, parameter) { + return Container( + child: Stack( + children: [ + Container( + width: 80.0, + height: 40.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(2)), + border: Border.all( + width: 1, + color: parameter.isSelected + ? theme + : Color.fromRGBO(222, 222, 222, 1)), + ), + child: Center( + child: Text( + "${parameter.buttonText}", + style: TextStyle( + color: parameter.isSelected + ? theme + : Color.fromRGBO(51, 51, 51, 1)), + ), + ), + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, // z轴阴影 + titleSpacing: 0, // 标题与其他控件的间隔 + backgroundColor: Colors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text( + '创建派对房间', + style: TextStyle( + color: Color.fromRGBO(51, 51, 51, 1), + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + backgroundColor: Colors.white, + resizeToAvoidBottomInset: false, + body: Container( + padding: EdgeInsets.all(15), + child: Form( + key: _formKey, + // autovalidateMode: AutovalidateMode.onUserInteraction, // 自动验证 + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + TextFormField( + controller: _roomName, + validator: (value) { + return value!.trim().isEmpty ? '请输入房间名' : null; + }, + style: TextStyle(fontSize: 16.0), + textInputAction: TextInputAction.next, + decoration: InputDecoration( + hintText: '输入房间名', + hintStyle: + TextStyle(color: Color.fromRGBO(187, 187, 187, 1)), + contentPadding: + EdgeInsets.all(15.0), // 输入内容距离上下左右距离,该属性控制高度 + fillColor: Color.fromRGBO(247, 248, 250, 1), // 输入框的背景填充颜色 + filled: true, + suffixIcon: IconButton( + icon: Image.asset( + 'images/createRoom/refresh.png', + width: 12.0, + height: 17.0, + ), + onPressed: () { + _roomName.clear(); + }, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide.none, + ), + ), + ), + SizedBox(height: 25.0), + + // 房间模式_单选框 + Container( + padding: EdgeInsets.fromLTRB(0, 0, 0, 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '房间模式', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 14.0, + color: Color.fromRGBO(153, 153, 153, 1), + ), + ), + SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: List.generate(patternList.length, + (index) { + return InkWell( + child: _buttonstyle( + Color.fromRGBO(21, 185, 208, 1), + patternList[index]), + onTap: () { + setState(() { + patternList.forEach( + (element) => element.isSelected = false); + print(patternList[index].buttonText); + patternList[index].isSelected = true; + }); + }, + ); + }), + ), + ], + ), + ), + + // 房间标签_单选框 + Container( + padding: EdgeInsets.fromLTRB(0, 0, 0, 25), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '房间标签', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 14.0, + color: Color.fromRGBO(153, 153, 153, 1), + ), + ), + SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: + List.generate(tagList.length, (index) { + return InkWell( + child: _buttonstyle( + Color.fromRGBO(239, 134, 146, 1), + tagList[index]), + onTap: () { + setState(() { + tagList.forEach( + (element) => element.isSelected = false); + print(tagList[index].buttonText); + tagList[index].isSelected = true; + }); + }, + ); + }), + ), + ], + ), + ), + + Divider( + height: 1.0, + color: Color.fromRGBO(235, 235, 235, 1), + ), + SizedBox(height: 25.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '房间密码', + style: TextStyle(color: Colors.black, fontSize: 16.0), + ), + CupertinoSwitch( + value: flag, + activeColor: Color.fromRGBO(21, 185, 208, 1), + onChanged: (value) { + setState(() { + flag = value; + print(flag); + }); + }, + ), + ], + ), + SizedBox(height: 40.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '修改密码', + style: TextStyle(color: Colors.black, fontSize: 16.0), + ), + GestureDetector( + onTap: () { + print('箭头图标被点击'); + }, + child: Icon( + size: 16.0, + Icons.arrow_forward_ios, + color: Color.fromRGBO(153, 153, 153, 1), + ), + ), + ], + ), + ], + ), + Column( + children: [ + Text( + '派对房间管理规范', + style: TextStyle( + color: Color.fromRGBO(153, 153, 153, 1), + fontSize: 12.0, + ), + ), + SizedBox(height: 5.0), + ElevatedButton( + child: const Text( + '创建', + style: TextStyle(fontSize: 16.0, color: Colors.white), + ), + style: ElevatedButton.styleFrom( + primary: Color.fromRGBO(21, 185, 208, 1), + minimumSize: Size(188, 50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + ), + onPressed: () { + if (_formKey.currentState!.validate()) { + print('表单验证通过!'); + } + }, + ), + SizedBox(height: 52.0), + ], + ), + ], + ), + ), + ), + ); + } +} + +class RadioModel { + bool isSelected; + final String buttonText; + RadioModel( + this.isSelected, + this.buttonText, + ); +} diff --git a/lib/views/createRoom/widget/CustomRadio.dart b/lib/views/createRoom/widget/CustomRadio.dart new file mode 100644 index 0000000..81b341e --- /dev/null +++ b/lib/views/createRoom/widget/CustomRadio.dart @@ -0,0 +1,89 @@ +// import 'package:flutter/material.dart'; + +// class CustomRadioWidget extends StatefulWidget { +// // final List list; + +// CustomRadioWidget({Key? key}) : super(key: key); + +// @override +// State createState() => _CustomRadioWidgetState(); +// } + +// class _CustomRadioWidgetState extends State { +// Widget _buttonstyle(theme, parameter) { +// return Container( +// child: Stack( +// children: [ +// Container( +// width: 80.0, +// height: 40.0, +// decoration: BoxDecoration( +// borderRadius: BorderRadius.all(Radius.circular(2)), +// border: Border.all( +// width: 1, +// color: parameter.isSelected +// ? theme +// : Color.fromRGBO(222, 222, 222, 1)), +// ), +// child: Center( +// child: Text( +// "${parameter.buttonText}", +// style: TextStyle( +// color: parameter.isSelected +// ? theme +// : Color.fromRGBO(51, 51, 51, 1)), +// ), +// ), +// ), +// ], +// ), +// ); +// } + +// @override +// Widget build(BuildContext context) { +// return Container( +// padding: EdgeInsets.fromLTRB(0, 0, 0, 25), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// '房间模式', +// textAlign: TextAlign.left, +// style: TextStyle( +// fontSize: 14.0, +// color: Color.fromRGBO(153, 153, 153, 1), +// ), +// ), +// SizedBox(height: 15), +// Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: List.generate(widget.list.length, (index) { +// return InkWell( +// child: _buttonstyle( +// Color.fromRGBO(21, 185, 208, 1), widget.list[index]), +// onTap: () { +// setState(() { +// widget.list +// .forEach((element) => element.isSelected = false); +// print(widget.list[index].buttonText); +// widget.list[index].isSelected = true; +// }); +// }, +// ); +// }), +// ), +// ], +// ), +// ); +// } +// } + +// class RadioModel { +// bool isSelected; +// final String buttonText; +// RadioModel( +// this.isSelected, +// this.buttonText, +// ); +// } diff --git a/lib/views/login/Login.dart b/lib/views/login/Login.dart new file mode 100644 index 0000000..913e0ed --- /dev/null +++ b/lib/views/login/Login.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import '../../utils/showToast.dart'; +import '../../utils/dioHttp.dart'; + +class LoginPage extends StatefulWidget { + LoginPage({Key? key}) : super(key: key); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + final _formKey = GlobalKey(); + final _userName = TextEditingController(); + final _password = TextEditingController(); + bool checked = true; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + elevation: 0, // z轴阴影 + titleSpacing: 0, // 标题与其他控件的间隔 + backgroundColor: Colors.white, + leading: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.black), + onPressed: () => Navigator.of(context).pop(), + ), + title: Text( + '登录', + style: TextStyle( + color: Color.fromRGBO(51, 51, 51, 1), + fontSize: 16, + fontWeight: FontWeight.bold), + ), + ), + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 30), + child: Form( + key: _formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: 80, + ), + Text( + '您好,欢迎登录', + style: TextStyle(fontSize: 28, fontWeight: FontWeight.w800), + ), + SizedBox( + height: 20, + ), + TextFormField( + controller: _userName, + validator: (value) { + return value!.trim().isEmpty ? '请输入账号' : null; + }, + decoration: InputDecoration( + labelText: "账号", + labelStyle: TextStyle(color: Color.fromRGBO(21, 185, 208, 1)), + enabledBorder: UnderlineInputBorder( + borderSide: + BorderSide(color: Color.fromRGBO(187, 187, 187, 1)), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Color.fromRGBO(21, 185, 208, 1), + ), + ), + ), + style: TextStyle(fontSize: 16), + keyboardType: TextInputType.phone, + ), + SizedBox( + height: 20, + ), + TextFormField( + controller: _password, + validator: (value) { + return value!.trim().isEmpty ? '请输入密码' : null; + }, + decoration: InputDecoration( + labelText: "密码", + labelStyle: TextStyle(color: Color.fromRGBO(21, 185, 208, 1)), + enabledBorder: UnderlineInputBorder( + borderSide: + BorderSide(color: Color.fromRGBO(187, 187, 187, 1)), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: Color.fromRGBO(21, 185, 208, 1), + ), + ), + ), + style: TextStyle(fontSize: 16), + obscureText: true, + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Checkbox( + value: checked, + activeColor: Color.fromRGBO(21, 185, 208, 1), + onChanged: (value) { + setState(() { + checked = !checked; + }); + }), + Text( + '同意', + style: TextStyle(fontSize: 14), + ), + Text( + '《服务协议》', + style: TextStyle(fontSize: 14, color: Colors.blue), + ), + Text( + '和', + style: TextStyle(fontSize: 14), + ), + Text('《隐私政策》', + style: TextStyle(fontSize: 14, color: Colors.blue)), + ], + ), + SizedBox( + height: 50, + ), + ElevatedButton( + child: Text( + '登录', + style: TextStyle(fontSize: 16.0, color: Colors.white), + ), + style: ElevatedButton.styleFrom( + primary: Color.fromRGBO(21, 185, 208, 1), + minimumSize: Size(MediaQuery.of(context).size.width, 50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30)), + ), + onPressed: () { + if (_formKey.currentState!.validate()) { + print('表单验证通过!'); + if (!checked) { + print('请勾选服务协议和隐私政策'); + } else { + dioHttp().dio.post('chat/api/auth/login', data: { + "username": _userName.text, + "password": _password.text, + }).then((res) { + print(res); + }); + + // print('请求登录接口业务逻辑'); + + // showToast("请求登录接口业务逻辑"); + + // showLoading(context, '加载中...'); + // Future.delayed(Duration(seconds: 3), () { + // Navigator.of(context).pop(); + // }); + + // showConfirmDialog(context, '确认xxxxxxx吗?', () { + // print('xxxxxxx'); + // }); + + } + } + }, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/views/palRoom/PalRoom.dart b/lib/views/palRoom/PalRoom.dart index e5108fb..9b240b5 100644 --- a/lib/views/palRoom/PalRoom.dart +++ b/lib/views/palRoom/PalRoom.dart @@ -28,7 +28,7 @@ class PalRoomPage extends StatefulWidget { class _PalRoomPageState extends State { final _users = []; // 用户id数组 - final _infoStrings = []; // 状态文字信息列表 + final _infoStrings = []; // 状态文字信息列表 bool viewPanel = true; // 是否显示状态文字信息列表 bool muted = false; // 是否开麦 bool volume = true; // 音量 @@ -49,14 +49,17 @@ class _PalRoomPageState extends State { @override void initState() { super.initState(); - _infoStrings.insert( - 0, '最新插入的item-${widget.userName}-${widget.channelName}-${widget.role}'); + _infoStrings.insert(0, { + 'type': 'system', + 'content': '信息-${widget.userName}-${widget.channelName}-${widget.role}' + }); + + initialize(); + if (widget.role == ClientRole.Broadcaster) { - // 判断若是主播,进入就初始化引擎并连接上麦 - initialize(); - _infoStrings.insert(0, '您身份为主播,已经上麦状态'); + _infoStrings.insert(0, {'type': 'system', 'content': '您身份为主播,已经上麦状态'}); } else if (widget.role == ClientRole.Audience) { - _infoStrings.insert(0, '您身份为观众,点击麦克风图标上麦'); // 判断若非主播,进入提示连麦 + _infoStrings.insert(0, {'type': 'system', 'content': '您身份为观众,点击麦克风图标上麦'}); } } @@ -72,8 +75,11 @@ class _PalRoomPageState extends State { // 判断appid是否存在 if (APP_ID.isEmpty) { setState(() { - _infoStrings.insert(0, 'APP ID缺失,请在settings.dart中提供您的APP ID'); - _infoStrings.insert(0, 'Agora引擎没有启动'); + _infoStrings.insert(0, { + 'type': 'system', + 'content': 'APP ID缺失,请在settings.dart中提供您的APP ID' + }); + _infoStrings.insert(0, {'type': 'system', 'content': 'Agora引擎没有启动'}); }); return; } @@ -85,11 +91,13 @@ class _PalRoomPageState extends State { await _engine.setAudioProfile(AudioProfile.SpeechStandard, AudioScenario.ChatRoomEntertainment); // 设置音频编码属性和音频场景 await _engine.setChannelProfile(ChannelProfile.LiveBroadcasting); - await _engine.setClientRole(widget.role!); + + if (widget.role == ClientRole.Broadcaster) { + await _engine.setClientRole(ClientRole.Broadcaster); + } + _engine.joinChannel(Token, widget.channelName, null, 0); // 加入频道 _addAgoraEventHandlers(); // 调用状态文字信息列表方法 - - _engine.joinChannel(Token, widget.channelName, null, 0); // 加入频道 } void _addAgoraEventHandlers() { @@ -98,20 +106,20 @@ class _PalRoomPageState extends State { // 错误 setState(() { final info = '错误 $code'; - _infoStrings.insert(0, info); + _infoStrings.insert(0, {'type': 'system', 'content': info}); }); }, joinChannelSuccess: (channel, uid, elapsed) { // 加入频道成功 setState(() { final info = '加入频道: $channel, uid: $uid'; - _infoStrings.insert(0, info); + _infoStrings.insert(0, {'type': 'system', 'content': info}); }); }, leaveChannel: (stats) { // 离开频道 setState(() { - _infoStrings.insert(0, '离开频道'); + _infoStrings.insert(0, {'type': 'system', 'content': '离开频道'}); _users.clear(); }); }, @@ -119,7 +127,7 @@ class _PalRoomPageState extends State { // 用户加入 setState(() { final info = '用户加入: $uid'; - _infoStrings.insert(0, info); + _infoStrings.insert(0, {'type': 'system', 'content': info}); _users.add(uid); }); }, @@ -127,7 +135,7 @@ class _PalRoomPageState extends State { // 用户离线 setState(() { final info = '用户离线: $uid , reason: $reason'; - _infoStrings.insert(0, info); + _infoStrings.insert(0, {'type': 'system', 'content': info}); _users.remove(uid); }); }, @@ -135,15 +143,95 @@ class _PalRoomPageState extends State { // 远程视频第一帧 setState(() { final info = 'First Remote Video Frame: $uid'; - _infoStrings.insert(0, info); + _infoStrings.insert(0, {'type': 'system', 'content': info}); + }); + }, + clientRoleChanged: (oldRole, newRole) { + // 直播场景下用户角色切换成功回调 + setState(() { + final info = '用户旧身份$oldRole,用户新身份$newRole'; + _infoStrings.insert(0, {'type': 'system', 'content': info}); }); }, )); } + // 头部 Widget _head() { + // 底部弹框组件 + _modelBottomSheet(index) async { + showModalBottomSheet( + context: context, + builder: (context) { + return Container( + height: 150.0, + child: Column( + children: [ + list[index]['content'] == false + ? ListTile( + title: Text( + "连线上麦", + textAlign: TextAlign.center, + ), + onTap: () { + setState(() { + for (var i = 0; i < list.length; i++) { + if (i == index) { + list[index]['content'] = true; + list[index]['name'] = '_users'; + list[index]['image'] = + 'images/palRoom/photo.png'; + } else { + list[i]['content'] = false; + list[i]['name'] = ''; + list[i]['image'] = 'images/palRoom/voice2.png'; + } + } + }); + + // 申请连麦需要改变身份为主播! + _engine.setClientRole(ClientRole.Broadcaster); + + Navigator.pop(context); + print("连线上麦:${list[index]['content']}"); + }, + ) + : ListTile( + title: Text( + "下麦", + textAlign: TextAlign.center, + ), + onTap: () { + setState(() { + list[index]['content'] = false; + list[index]['name'] = ''; + list[index]['image'] = 'images/palRoom/voice2.png'; + }); + + // 下麦再将身份改回为观众(下麦不代表离开频道) + _engine.setClientRole(ClientRole.Audience); + + Navigator.pop(context); + print("下麦:${list[index]['content']}"); + }, + ), + ListTile( + title: Text( + "取消", + textAlign: TextAlign.center, + ), + onTap: () { + Navigator.pop(context); + }), + ], + ), + ); + }, + ); + } + return Container( - child: Column( + child: ListView( children: [ Container( padding: EdgeInsets.fromLTRB(0, 27, 0, 5), @@ -187,9 +275,9 @@ class _PalRoomPageState extends State { shrinkWrap: true, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisSpacing: 40, - mainAxisSpacing: 35, + mainAxisSpacing: 30, crossAxisCount: 4, - childAspectRatio: 1 / 1.2, + childAspectRatio: 1 / 1.25, ), itemCount: list.length, itemBuilder: (BuildContext context, int index) { @@ -200,7 +288,8 @@ class _PalRoomPageState extends State { // 观众身份的时候点击上麦克图标 _modelBottomSheet(index); // 调用底部弹框方法 } else { - _infoStrings.insert(0, '您身份为主播,已经上麦状态'); + _infoStrings.insert( + 0, {'type': 'system', 'content': '您身份为主播,已经上麦状态'}); } }); }, @@ -239,19 +328,45 @@ class _PalRoomPageState extends State { ); } + // 消息列表 Widget _planel() { + _filtering(type, content) { + switch (type) { + case 'system': + return Container( + child: Text( + '系统公告:$content', + style: TextStyle(color: Color.fromRGBO(155, 154, 170, 1)), + ), + ); + case 'speak': + return Container( + child: Text( + '聊天消息:$content', + style: TextStyle(color: Color.fromRGBO(155, 154, 170, 1)), + ), + ); + case 'gift': + return Container( + child: Text( + '礼物消息:$content', + style: TextStyle(color: Color.fromRGBO(155, 154, 170, 1)), + ), + ); + } + } + return Visibility( visible: viewPanel, child: Container( - padding: EdgeInsets.symmetric(vertical: 48), + padding: EdgeInsets.symmetric(vertical: 60), alignment: Alignment.bottomCenter, child: FractionallySizedBox( heightFactor: 0.5, child: Container( - // decoration: BoxDecoration( - // color: Colors.black, - // ), - // padding: EdgeInsets.symmetric(vertical: 48), + decoration: BoxDecoration( + color: Colors.black, + ), child: ListView.builder( reverse: true, itemCount: _infoStrings.length, @@ -274,12 +389,8 @@ class _PalRoomPageState extends State { color: Color.fromRGBO(50, 51, 71, 1), borderRadius: BorderRadius.circular(5), ), - child: Text( - _infoStrings[index], - style: TextStyle( - color: Color.fromRGBO(155, 154, 170, 1), - ), - ), + child: _filtering(_infoStrings[index]['type'], + _infoStrings[index]['content']), ), ), ], @@ -293,6 +404,7 @@ class _PalRoomPageState extends State { ); } + // 底部输入框控制台 Widget _footer() { final _input = TextEditingController(); return Container( @@ -431,12 +543,14 @@ class _PalRoomPageState extends State { ), SizedBox(width: 5), Container( - padding: EdgeInsets.fromLTRB(6, 1, 6, 1), + padding: EdgeInsets.fromLTRB(6, 2, 6, 2), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(2)), color: Color.fromRGBO(47, 47, 59, 1), ), child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( '在线:121', @@ -489,72 +603,4 @@ class _PalRoomPageState extends State { ), ); } - - // 底部弹框方法 - _modelBottomSheet(index) async { - showModalBottomSheet( - context: context, - builder: (context) { - return Container( - height: 150.0, - child: Column( - children: [ - list[index]['content'] == false - ? ListTile( - title: Text( - "连线上麦", - textAlign: TextAlign.center, - ), - onTap: () { - setState(() { - for (var i = 0; i < list.length; i++) { - if (i == index) { - list[index]['content'] = true; - list[index]['name'] = '_users'; - list[index]['image'] = 'images/palRoom/photo.png'; - } else { - list[i]['content'] = false; - list[i]['name'] = ''; - list[i]['image'] = 'images/palRoom/voice2.png'; - } - } - }); - - initialize(); - _engine.joinChannel( - Token, widget.channelName, null, 0); // 加入频道 - Navigator.pop(context); - print("连线上麦:${list[index]['content']}"); - }, - ) - : ListTile( - title: Text( - "离开下麦", - textAlign: TextAlign.center, - ), - onTap: () { - setState(() { - list[index]['content'] = false; - list[index]['name'] = ''; - list[index]['image'] = 'images/palRoom/voice2.png'; - }); - _engine.leaveChannel(); - print("离开下麦:${list[index]['content']}"); - Navigator.pop(context); - }, - ), - ListTile( - title: Text( - "取消", - textAlign: TextAlign.center, - ), - onTap: () { - Navigator.pop(context); - }), - ], - ), - ); - }, - ); - } } diff --git a/lib/views/party/Party.dart b/lib/views/party/Party.dart index 7e1e69e..a016e68 100644 --- a/lib/views/party/Party.dart +++ b/lib/views/party/Party.dart @@ -83,7 +83,7 @@ class _PartyPageState extends State { actions: [ IconButton( onPressed: () { - print('create图标被点击'); + Navigator.pushNamed(context, 'CreateRoom'); }, icon: Image.asset( 'images/party/create.png', diff --git a/pubspec.lock b/pubspec.lock index ebf319a..7753ed7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,13 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" + dio: + dependency: "direct main" + description: + name: dio + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.6" fake_async: dependency: transitive description: @@ -86,6 +93,20 @@ packages: description: flutter source: sdk version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.1.8" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.1" js: dependency: transitive description: @@ -203,6 +224,20 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.4.9" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.1" + validators: + dependency: "direct main" + description: + name: validators + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.0" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e79c81d..29c60ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,9 @@ dependencies: cupertino_icons: ^1.0.2 agora_rtc_engine: ^5.2.0 permission_handler: ^8.3.0 + validators: ^3.0.0 + dio: ^4.0.6 + fluttertoast: ^7.1.6 dev_dependencies: flutter_test: @@ -107,6 +110,8 @@ flutter: - images/palRoom/whiteLeft.png - images/palRoom/whiteRight.png + - images/createRoom/refresh.png + # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware