diff --git a/android/app/build.gradle b/android/app/build.gradle index c8c8611..7f964be 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -39,14 +39,24 @@ android { targetSdkVersion 32 versionCode flutterVersionCode.toInteger() versionName flutterVersionName +// multiDexEnabled true +// manifestPlaceholders = [ +// JPUSH_PKGNAME : applicationId, +// JPUSH_APPKEY : "", // NOTE: JPush 上注册的包名对应的 Appkey. +// JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可. +// ] } + buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } +// debug { +// signingConfig signingConfigs.release +// } } } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 402f27a..d1a8d3b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ SizedBox(width: w); + + Widget get hb => SizedBox(height: w); +} \ No newline at end of file diff --git a/lib/extensions/string_extension.dart b/lib/extensions/string_extension.dart new file mode 100644 index 0000000..332e1d0 --- /dev/null +++ b/lib/extensions/string_extension.dart @@ -0,0 +1,71 @@ + + +import 'package:lpinyin/lpinyin.dart'; + +// import '../constants/api/api.dart'; + +extension ImageOnString on String? { + // String get imageWithHost => '${API.imageHost}/$this'; + + // String sizeImage(int width, [int? height]) { + // var parts = []; + // parts.add('w=$width'); + // if (height != null) parts.add('h=$height'); + // return '$imageWithHost@${parts.join('&')}'; + // } + + String get toSnake { + RegExp exp = RegExp(r'(?<=[a-z])[A-Z]'); + if (this == null) { + return ''; + } + return this! + .replaceAllMapped(exp, (Match m) => ('_${m.group(0)!}')) + .toLowerCase(); + } + + String get pinyin => PinyinHelper.getPinyinE(this ?? ''); + + String get tag => pinyin.substring(0, 1).toUpperCase(); + + int get minPrice { + if (this == null || this!.isEmpty) { + return 0; + } else if (this!.contains('不限')) { + return 0; + } else if (this!.contains('以下')) { + return 0; + } else if (this!.contains('以上')) { + return int.parse(this!.substring(0, this!.length - 3)) * 10000; + } else { + { + var list = this!.split('-'); + return int.parse(list[0]) * 10000; + } + } + } + + int get maxPrice { + if (this == null || this!.isEmpty) { + return 0; + } else if (this!.contains('不限')) { + return 0; + } else if (this!.contains('以下')) { + return int.parse(this!.substring(0, this!.length - 3)) * 10000; + } + if (this!.contains('以上')) { + return 0; + } else { + var list = this!.split('-'); + return int.parse(list[1].substring(0, list[1].length - 1)) * 10000; + } + } + + int get maxMile { + if (this == null || this!.isEmpty) { + return 0; + } else { + return int.parse(this!.substring(0, this!.length - 4)); + } + } +} diff --git a/lib/extensions/wigget_list_ext.dart b/lib/extensions/wigget_list_ext.dart new file mode 100644 index 0000000..87283ac --- /dev/null +++ b/lib/extensions/wigget_list_ext.dart @@ -0,0 +1,41 @@ + +import 'package:flutter/material.dart'; + +import 'num_ext.dart'; + +extension WidgetListExt on List { + List sepWidget({Widget? separate}) { + if (isEmpty) return []; + return List.generate(length * 2 - 1, (index) { + if (index.isEven) { + return this[index ~/ 2]; + } else { + return separate ?? 10.wb; + } + }); + } +} + +extension OddListExt on List { + List oddList() { + List _newList = []; + for (var element in this) { + if (indexOf(element).isEven) { + _newList.add(element); + } + } + return _newList; + } +} + +extension EvenListExt on List { + List evenList() { + List _newList = []; + forEach((element) { + if (indexOf(element).isOdd) { + _newList.add(element); + } + }); + return _newList; + } +} diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart new file mode 100644 index 0000000..377f5e6 --- /dev/null +++ b/lib/gen/assets.gen.dart @@ -0,0 +1,147 @@ +/// GENERATED CODE - DO NOT MODIFY BY HAND +/// ***************************************************** +/// FlutterGen +/// ***************************************************** + +// coverage:ignore-file +// ignore_for_file: type=lint +// ignore_for_file: directives_ordering,unnecessary_import + +import 'package:flutter/widgets.dart'; + +class $AssetsIconsGen { + const $AssetsIconsGen(); + + /// File path: assets/icons/home_ selected.png + AssetGenImage get homeSelected => + const AssetGenImage('assets/icons/home_ selected.png'); + + /// File path: assets/icons/home_noSelected.png + AssetGenImage get homeNoSelected => + const AssetGenImage('assets/icons/home_noSelected.png'); + + /// File path: assets/icons/my_noselected.png + AssetGenImage get myNoselected => + const AssetGenImage('assets/icons/my_noselected.png'); + + /// File path: assets/icons/my_selected.png + AssetGenImage get mySelected => + const AssetGenImage('assets/icons/my_selected.png'); + + /// File path: assets/icons/permissions.png + AssetGenImage get permissions => + const AssetGenImage('assets/icons/permissions.png'); + + /// File path: assets/icons/privacy.png + AssetGenImage get privacy => const AssetGenImage('assets/icons/privacy.png'); + + /// File path: assets/icons/switch1.png + AssetGenImage get switch1 => const AssetGenImage('assets/icons/switch1.png'); + + /// File path: assets/icons/weixin.png + AssetGenImage get weixin => const AssetGenImage('assets/icons/weixin.png'); + + /// File path: assets/icons/zhifubao.png + AssetGenImage get zhifubao => + const AssetGenImage('assets/icons/zhifubao.png'); +} + +class $AssetsImagesGen { + const $AssetsImagesGen(); + + /// File path: assets/images/answer.png + AssetGenImage get answer => const AssetGenImage('assets/images/answer.png'); + + /// File path: assets/images/banner.png + AssetGenImage get banner => const AssetGenImage('assets/images/banner.png'); + + /// File path: assets/images/bg.png + AssetGenImage get bg => const AssetGenImage('assets/images/bg.png'); + + /// File path: assets/images/home_bg.png + AssetGenImage get homeBg => const AssetGenImage('assets/images/home_bg.png'); + + /// File path: assets/images/portrait.png + AssetGenImage get portrait => + const AssetGenImage('assets/images/portrait.png'); + + /// File path: assets/images/refused.png + AssetGenImage get refused => const AssetGenImage('assets/images/refused.png'); + + /// File path: assets/images/vipbanner.png + AssetGenImage get vipbanner => + const AssetGenImage('assets/images/vipbanner.png'); + + /// File path: assets/images/vipbg.png + AssetGenImage get vipbg => const AssetGenImage('assets/images/vipbg.png'); +} + +class Assets { + Assets._(); + + static const $AssetsIconsGen icons = $AssetsIconsGen(); + static const $AssetsImagesGen images = $AssetsImagesGen(); +} + +class AssetGenImage { + const AssetGenImage(this._assetName); + + final String _assetName; + + Image image({ + Key? key, + AssetBundle? bundle, + ImageFrameBuilder? frameBuilder, + ImageErrorWidgetBuilder? errorBuilder, + String? semanticLabel, + bool excludeFromSemantics = false, + double? scale, + double? width, + double? height, + Color? color, + Animation? opacity, + BlendMode? colorBlendMode, + BoxFit? fit, + AlignmentGeometry alignment = Alignment.center, + ImageRepeat repeat = ImageRepeat.noRepeat, + Rect? centerSlice, + bool matchTextDirection = false, + bool gaplessPlayback = false, + bool isAntiAlias = false, + String? package, + FilterQuality filterQuality = FilterQuality.low, + int? cacheWidth, + int? cacheHeight, + }) { + return Image.asset( + _assetName, + key: key, + bundle: bundle, + frameBuilder: frameBuilder, + errorBuilder: errorBuilder, + semanticLabel: semanticLabel, + excludeFromSemantics: excludeFromSemantics, + scale: scale, + width: width, + height: height, + color: color, + opacity: opacity, + colorBlendMode: colorBlendMode, + fit: fit, + alignment: alignment, + repeat: repeat, + centerSlice: centerSlice, + matchTextDirection: matchTextDirection, + gaplessPlayback: gaplessPlayback, + isAntiAlias: isAntiAlias, + package: package, + filterQuality: filterQuality, + cacheWidth: cacheWidth, + cacheHeight: cacheHeight, + ); + } + + String get path => _assetName; + + String get keyName => _assetName; +} diff --git a/lib/main.dart b/lib/main.dart index f059485..7f9466e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,12 @@ import 'dart:async'; import 'package:call_log/call_log.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get_navigation/src/root/get_material_app.dart'; +import 'package:project_telephony/ui/login/login_page.dart'; + import 'package:telephony/telephony.dart'; onBackgroundMessage(SmsMessage message) { @@ -12,6 +17,9 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); await initializeService(); runApp(const MyApp()); + SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, //状态栏背景色 + statusBarIconBrightness: Brightness.dark); //状态栏字体颜色 } Future initializeService() async { @@ -32,25 +40,29 @@ Future initializeService() async { void onIosBackground() { WidgetsFlutterBinding.ensureInitialized(); - print('FLUTTER BACKGROUND FETCH'); + // print('FLUTTER BACKGROUND FETCH'); } void onStart() { int flag = 0; - String? phoneNum,callState; + String? phoneNum, callState; WidgetsFlutterBinding.ensureInitialized(); Timer.periodic(const Duration(seconds: 1), (timer) async { CallState state = await Telephony.instance.callState; - callState=state.name; - print(callState!+"$flag"); + callState = state.name; + // print(callState!+"$flag"); if (callState == "IDLE") { if (flag != 0) { flag = 0; final Iterable result = await CallLog.query(); - phoneNum = await result.first.number; - print(phoneNum); - Phone.telephony.sendSms(to: phoneNum!, message: "hello",isMultipart:true,); + phoneNum = result.first.number; + // print(phoneNum); + Phone.telephony.sendSms( + to: phoneNum!, + message: "hello", + isMultipart: true, + ); } } else if (callState == "RINGING") { flag++; @@ -60,7 +72,7 @@ void onStart() { }); } -class Phone{ +class Phone { static Telephony telephony = Telephony.instance; } @@ -78,6 +90,13 @@ class _MyAppState extends State { void initState() { super.initState(); final service = FlutterBackgroundService(); + // JPush jPush=JPush(); + // jPush.setup( + // appKey: "", + // channel: "theChannel", + // production: false, + // debug: true + // ); service.start(); } @@ -104,23 +123,31 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Plugin example app'), - ), - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: () async { - Phone.telephony.sendSms(to: "10086", message: "1"); - }, - child: const Text('sms')), - ], - ), - ), - ); + return MediaQuery( + data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window), + child: ScreenUtilInit( + designSize: const Size(750, 1334), + builder: (context, child) { + return const AnnotatedRegion( + value: SystemUiOverlayStyle( + statusBarColor: Colors.transparent, //状态栏背景色 + statusBarIconBrightness: Brightness.dark), + child: GetMaterialApp( + // get.testmode=true, + debugShowCheckedModeBanner: false, + home: LoginPage(), + // supportedLocales: [Locale('zh')], + // locale: Locale('zh'), + // builder: ( context,child){ + // ScreenUtil.setContext(context); + // return MediaQuery( + // //设置文字大小不随系统设置改变 + // data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + // // child: BotToastInit().call(context, child), + // ); + // }, + )); + }, + )); } } - diff --git a/lib/ui/home/content_details_page.dart b/lib/ui/home/content_details_page.dart new file mode 100644 index 0000000..5aa879d --- /dev/null +++ b/lib/ui/home/content_details_page.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:project_telephony/utils/headers.dart'; + +import '../../base/base_style.dart'; +import '../widget/plone_back_button.dart'; +import '../widget/plone_bottom.dart'; + +class ContentDetailsPage extends StatefulWidget { + const ContentDetailsPage({Key? key}) : super(key: key); + + @override + _ContentDetailsPageState createState() => _ContentDetailsPageState(); +} + +class _ContentDetailsPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + '选择短信内容', + style: Theme.of(context).textTheme.headline6, + ), + leading: const CloudBackButton(isSpecial: true), + backgroundColor: kForeGroundColor + ), + body: _getBox(), + ); + } + _getBox(){ + return Column(children: [ + Container( + width: 622.w, + height: 960.w, + decoration: BoxDecoration(color: BaseStyle.colorcccccc,borderRadius: BorderRadius.circular(16)), + margin: EdgeInsets.only(left: 32.w,right: 32.w,top: 32.w,bottom: 298.w), + child: const Text('请输入短信内容'),), + PloneBottom(onTap: (){ + + },text: "保存",) + ]); + } +} + diff --git a/lib/ui/home/content_page.dart b/lib/ui/home/content_page.dart new file mode 100644 index 0000000..1fe3d75 --- /dev/null +++ b/lib/ui/home/content_page.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; +import 'package:project_telephony/base/base_style.dart'; +import 'package:project_telephony/ui/home/content_details_page.dart'; +import 'package:project_telephony/ui/widget/plone_back_button.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class ContentPage extends StatefulWidget { + const ContentPage({Key? key}) : super(key: key); + + @override + _ContentPageState createState() => _ContentPageState(); +} + +class _ContentPageState extends State { + List textList = ['欢迎你的来电', '祝您生活愉快', '感谢您的来电我们会尽快处理的']; + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + '选择短信内容', + style: Theme.of(context).textTheme.headline6, + ), + leading: const CloudBackButton(isSpecial: true), + backgroundColor: kForeGroundColor, + ), + body: Column(children: [ + Expanded( + child: _getList(), + ), + _getBox2("自定义短信内容"), + 32.hb + ]), + ); + } + + _getList() { + return ListView.builder( + itemBuilder: (context, index) { + return _getBox(textList[index], index == 0); + }, + itemCount: textList.length, + ); + } + + _getBox(String content, bool pd) { + return Container( + width: 686.w, + height: 122.w, + margin: EdgeInsets.only(top: 32.w, left: 32.w, right: 32.w), + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 30.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: pd ? Colors.blue : Colors.white, + ), + child: Text(content, + style: TextStyle( + fontSize: BaseStyle.fontSize28, + color: pd ? const Color(0xFFF9F9F9) : BaseStyle.color333333)), + ); + } + + _getBox2( + String content, + ) { + return GestureDetector( + onTap: () { + Get.to(() => const ContentDetailsPage()); + }, + child: Container( + width: 686.w, + height: 122.w, + margin: EdgeInsets.only(top: 32.w, left: 32.w, right: 32.w), + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 30.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Colors.white, + ), + child: Text( + content, + style: TextStyle( + fontSize: BaseStyle.fontSize28, color: const Color(0xFF000000)), + )), + ); + } +} diff --git a/lib/ui/home/home_page.dart b/lib/ui/home/home_page.dart new file mode 100644 index 0000000..2ad87f8 --- /dev/null +++ b/lib/ui/home/home_page.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; +import 'package:project_telephony/ui/home/content_page.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class HomePage extends StatefulWidget { + const HomePage({Key? key}) : super(key: key); + + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: Column( + children: [ + Container( + //margin:EdgeInsets.symmetric(horizontal: 32.w,vertical: 16.w), + child: Image.asset( + Assets.images.homeBg.path, + )), + 12.hb, + _getBody(), + ], + ), + ); + } + + _getBody() { + return Container( + padding: EdgeInsets.symmetric(horizontal: 32.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "短信助手", + style: TextStyle( + fontSize: 64.sp, + color: Colors.black, + fontWeight: FontWeight.bold), + ), + 32.hb, + Text( + "希望能成为您的短信小助手", + style: TextStyle(fontSize: 32.sp, color: const Color(0xFF999999)), + ), + 50.hb, + _getContainer("接听后", "编辑接听后发送的短信内容", Assets.images.answer.path, + const Color(0xFF74BCFF), const Color(0xFF1890FF)), + 30.hb, + _getContainer("拒接/未接后", "编辑拒接/未接后发送的短信内容", Assets.images.refused.path, + const Color(0xFF72E4C8), const Color(0xFF13CA9D)) + ], + ), + ); + } + + _getContainer(String title, String text, String image, Color cl1, Color cl2) { + return GestureDetector( + onTap: () { + print(title); + if (title == "接听后") { + Get.to(() => const ContentPage()); + // print("接听"); + } else { + print("未接听"); + } + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.w), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [cl1, cl2])), + child: Row( + children: [ + Container( + width: 532.w, + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, + style: TextStyle( + fontSize: 36.sp, + color: const Color(0xFFFFFFFF), + fontWeight: FontWeight.bold)), + 24.hb, + Text( + text, + style: TextStyle( + fontSize: 28.sp, + color: const Color(0xFFFFFFFF).withOpacity(0.6)), + ) + ], + ), + ), + 45.wb, + Image.asset( + image, + width: 108.w, + height: 66.h, + ) + ], + ), + ), + ); + } +} diff --git a/lib/ui/login/login_page.dart b/lib/ui/login/login_page.dart new file mode 100644 index 0000000..e1bff7f --- /dev/null +++ b/lib/ui/login/login_page.dart @@ -0,0 +1,369 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:project_telephony/utils/headers.dart'; + +import '../../base/base_style.dart'; +import '../tab_navigator.dart'; +import '../widget/image_scaffold.dart'; +import '../widget/plone_bottom.dart'; + +class LoginPage extends StatefulWidget { + const LoginPage({Key? key}) : super(key: key); + + @override + _LoginPageState createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + // String _operator=""; + // String _phone=""; + // bool _chooseAgreement = false; + // bool _getCodeEnable = true; + // late Timer _timer; + // + // ///时钟 + // String _countDownStr = "发送验证码"; + // + // ///初始化文本 + // int _countDownNum = 59; + + ///时间倒计数值 + late final TextEditingController _phoneController = + TextEditingController(text: ""); + late final TextEditingController _smsCodeController = + TextEditingController(text: ""); + + // // late FocusNode _smsCodeFocusNode=FocusNode (canRequestFocus: ); + // bool _cantSelected = false; + @override + void initState() { + super.initState(); + // _operator = '中国移动认证'; + // _phone = '12345678909'; + } + + @override + Widget build(BuildContext context) { + return CloudScaffold( + systemStyle: const SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + ), + path: Assets.images.bg.path, + appbar: Row( + children: [ + Padding( + padding: EdgeInsets.only(top: 88.w, left: 8.w), + child: const Icon( + CupertinoIcons.chevron_back, + color: Colors.black, + ), + ), + Padding( + padding: EdgeInsets.only(left: 239.w, top: 88.w), + child: Text( + "登录/注册", + style: TextStyle( + fontSize: BaseStyle.fontSize34, + color: BaseStyle.color333333, + fontWeight: FontWeight.bold), + )) + ], + ), + extendBody: true, + body: Container( + padding: EdgeInsets.only(left: 64.w, right: 64.w, top: 138.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "短信帮手", + style: TextStyle( + fontSize: 64.sp, + fontWeight: FontWeight.bold, + color: Colors.black), + ), + 32.hb, + Text( + "希望能成为您的短信小助手", + style: TextStyle(fontSize: 32.sp, color: const Color(0xFF999999)), + ), + 80.hb, + _phoneTFWidget(), + _codeWidget(), + 112.hb, + PloneBottom( + onTap: () { + Get.to(() => const TabNavigator()); + }, + text: "立即登录", + ), + 32.hb, + _getText() + ], + ), + ), + ); + } +// 输入手机号 + + _phoneTFWidget() { + return Container( + alignment: Alignment.centerLeft, + // padding: EdgeInsets.symmetric(horizontal: 72.w), + height: 40 * 2.h, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: TextField( + onChanged: (String phone) { + // setState(() { + // if (phone.length >= 11) { + // _getCodeEnable = true; + // } else { + // _getCodeEnable = false; + // } + // if (kDebugMode) { + // //print(_loginEnable); + // } + // }); 判断手机号长度 + }, + // enabled: false, + controller: _phoneController, + // focusNode: _phoneFocusNode, + keyboardType: TextInputType.number, + style: TextStyle( + fontSize: 36.sp, color: const Color(0xFF999999)), + inputFormatters: [ + LengthLimitingTextInputFormatter(11), + ], + cursorColor: Colors.black, + decoration: InputDecoration( + // contentPadding: EdgeInsets.only( + // left: rSize(10), top: rSize(13)), + // prefixIcon: Container( + // // padding: EdgeInsets.only(right: 10.w), + // width: 50.w, + // alignment: Alignment.centerLeft, + // child: Text( + // "+86", + // style: TextStyle( + // fontSize: 32.sp, + // color: const Color(0xFF000000)), + // ), + // ), + prefixText: "+86", + prefixStyle: TextStyle( + color: Colors.black, + ), + enabledBorder: const UnderlineInputBorder( + // 不是焦点的时候颜色 + borderSide: BorderSide( + color: Color(0xffdddddd), + ), + ), + // focusedBorder: const UnderlineInputBorder( // 焦点集中的时候颜色 + // borderSide: BorderSide( + // color: Color(0x19000000) + // ), + // ), + //border: InputBorder.none, + hintText: + "请输入手机号", // hintText: _phone.replaceFirst(RegExp(r'\d{4}'), '****', 3), + hintStyle: TextStyle( + color: const Color(0xFFCCCCCC), fontSize: 32.sp), + suffixIcon: GestureDetector( + onTap: () {}, + // onTap: !_getCodeEnable + // ? () {} + // : () async { + // // await apiClient + // // .request(API.login.phoneCode, data: { + // // 'phone': + // // UserTool.userProvider.userInfo.phone, + // // }); + // _beginCountDown(); + // if (_cantSelected) return; + // _cantSelected = true; + // Future.delayed(const Duration(seconds: 1), + // () { + // _cantSelected = false; + // }); + // }, + child: Container( + width: 180.w, + alignment: Alignment.centerRight, + color: Colors.transparent, + child: const Text("获取验证码" + // _countDownStr, + // style: TextStyle( + // color: _getCodeEnable + // ? kPrimaryColor + // : BaseStyle.colorcccccc), + ), + ), + )), + ), + ), + ], + ), + ), + //_bottomLineWidget(), + ], + ), + ); + } + +// 输入验证码 + _codeWidget() { + return Container( + alignment: Alignment.centerLeft, + //margin: EdgeInsets.symmetric(horizontal: 20.w), + // padding: EdgeInsets.symmetric(horizontal: 72.w), + height: 40 * 2.h, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: TextField( + onChanged: (String phone) { + setState(() {}); + }, + controller: _smsCodeController, + // focusNode: _smsCodeFocusNode, + keyboardType: TextInputType.number, + style: TextStyle( + fontSize: 36.sp, color: BaseStyle.color999999), + inputFormatters: [ + LengthLimitingTextInputFormatter(4), + ], + cursorColor: Colors.black, + decoration: InputDecoration( + // prefixIcon: Container( + // width: 10.w, + // alignment: Alignment.centerLeft, + // child: Text( + // "验证码", + // style: TextStyle( + // fontSize: 32.sp, + // color: const Color(0xFF000000)), + // ), + // ), + prefixText: "验证码", + prefixStyle: const TextStyle( + color: Colors.black, + ), + enabledBorder: const UnderlineInputBorder( + // 不是焦点的时候颜色 + borderSide: BorderSide( + color: BaseStyle.color999999, + ), + ), + hintText: "请输入验证码", + hintStyle: TextStyle( + color: BaseStyle.colorcccccc, fontSize: 36.sp), + )), + ), + ], + ), + ), + //_bottomLineWidget(), + ], + ), + ); + } + +// // 倒计时 +// _beginCountDown() { +// ///开始倒计时 +// setState(() { +// _getCodeEnable = false; +// _countDownStr = "重新获取($_countDownNum)"; +// }); +// _timer = Timer.periodic(const Duration(seconds: 1), (timer) { +// if (!mounted) { +// return; +// } +// setState(() { +// if (_countDownNum == 0) { +// _countDownNum = 59; +// _countDownStr = "获取验证码"; +// _getCodeEnable = true; +// _timer.cancel(); +// return; +// } +// _countDownStr = "重新获取(${_countDownNum--})"; +// }); +// }); +// } + +// 协议 + _recognizer(context, int type) { + final TapGestureRecognizer recognizer = TapGestureRecognizer(); + recognizer.onTap = () { + if (kDebugMode) { + print("点击协议了"); + } + + ///跳转到用户协议页面 + }; + return recognizer; + } + + _getText() { + return GestureDetector( + onTap: () { + // _chooseAgreement = !_chooseAgreement; + setState(() {}); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 50.w, + height: 50.w, + padding: EdgeInsets.only(top: 6.w, right: 5.w), + child: const Icon(CupertinoIcons.circle, + size: 18, color: Color(0xFFdddddd))), + // !_chooseAgreement + // ? const Icon(CupertinoIcons.circle, + // size: 18, color: Color(0xFFdddddd)) + // : const Icon(CupertinoIcons.checkmark_circle, + // size: 18, color: Colors.red)), + RichText( + text: TextSpan( + text: "我已阅读并同意", + style: TextStyle( + color: BaseStyle.colorcccccc, fontSize: 12 * 2.sp), + children: [ + TextSpan( + text: '《用户服务协议》', + style: TextStyle(color: kPrimaryColor, fontSize: 12 * 2.sp), + recognizer: _recognizer(context, 2)), + TextSpan( + text: "和", + style: TextStyle( + color: BaseStyle.colorcccccc, fontSize: 12 * 2.sp), + ), + TextSpan( + text: '《隐私协议》', + style: TextStyle(color: kPrimaryColor, fontSize: 12 * 2.sp), + recognizer: _recognizer(context, 1)), + ])), + ], + ), + ); + } +} diff --git a/lib/ui/tab_navigator.dart b/lib/ui/tab_navigator.dart new file mode 100644 index 0000000..5393997 --- /dev/null +++ b/lib/ui/tab_navigator.dart @@ -0,0 +1,97 @@ +import 'package:bot_toast/bot_toast.dart'; +import 'package:flutter/material.dart'; +import 'package:project_telephony/ui/user/user_page.dart'; +import 'package:project_telephony/utils/headers.dart'; + +import 'home/home_page.dart'; + +class TabNavigator extends StatefulWidget { + final int? index; + const TabNavigator({Key? key, this.index}) : super(key: key); + + @override + _TabNavigatorState createState() => _TabNavigatorState(); +} + +class _TabNavigatorState extends State + with SingleTickerProviderStateMixin { + TabController? _tabController; + int _pageIndex = 0; + DateTime? _lastPressed; + +// 页面列表 + List _pages = []; + + @override + void initState() { + super.initState(); + //页面加载调用 + Future.delayed(const Duration(milliseconds: 0), () async { + // Hive.initFLutter; + // await HiveStore.init() + }); + _pages = [const HomePage(), const UserPage()]; + _tabController = TabController( + length: _pages.length, vsync: this, initialIndex: widget.index ?? 0); + } + + //选中与未选中图标样式 + _buildBottomBar(String title, String unSelected, String selected) { + return BottomNavigationBarItem( + icon: Image.asset( + unSelected, + height: 44.w, + width: 44.w, + ), + activeIcon: Image.asset( + selected, + height: 44.w, + width: 44.w, + ), + label: title, + ); + } + + @override + Widget build(BuildContext context) { + //底部导航来 + List _bottomNav = [ + _buildBottomBar("首页", Assets.icons.homeNoSelected.path, + Assets.icons.homeSelected.path), + _buildBottomBar( + "我的", Assets.icons.myNoselected.path, Assets.icons.mySelected.path) + ]; + return Scaffold( + body: WillPopScope( + onWillPop: () async { + if (_lastPressed == null || + DateTime.now().difference(_lastPressed!) > + const Duration(seconds: 1)) { + //两次点击间隔超过1秒重新计算 + _lastPressed = DateTime.now(); + BotToast.showText(text: '再点击一次返回退出'); + return false; + } + // 否则关闭APP + return true; + }, + child: TabBarView( + children: _pages, + controller: _tabController, + physics: const NeverScrollableScrollPhysics(), + ), + ), + bottomNavigationBar: BottomNavigationBar( + items: _bottomNav, + backgroundColor: Colors.white, + currentIndex: _pageIndex, + selectedFontSize: 20.sp, + unselectedFontSize: 20.sp, + onTap: (index) { + _tabController!.animateTo(index, curve: Curves.easeInOutCubic); + setState(() => _pageIndex = index); + }, + ), + ); + } +} diff --git a/lib/ui/user/members_page.dart b/lib/ui/user/members_page.dart new file mode 100644 index 0000000..2ab5904 --- /dev/null +++ b/lib/ui/user/members_page.dart @@ -0,0 +1,305 @@ +import 'package:flutter/material.dart'; + +import 'package:project_telephony/base/base_style.dart'; +import 'package:project_telephony/ui/widget/check_radio.dart'; +// import 'package:project_telephony/ui/widget/check_radio.dart'; + +import 'package:project_telephony/ui/widget/plone_back_button.dart'; +import 'package:project_telephony/ui/widget/plone_bottom.dart'; +import 'package:project_telephony/ui/widget/putup_widget.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class MembersPage extends StatelessWidget { + MembersPage({Key? key}) : super(key: key); + ChooseItems? _chooseItem; + List? data; + final List _piceList = [ + ChooseItems( + month: 12, + pice: 10, + ), + ChooseItems( + month: 1, + pice: 1, + ), + ]; + int _selectIndex = 0; + List payWay = [ + { + 'payName': '微信支付', + 'payUrl': Assets.icons.weixin.path, + }, + {'payName': '支付宝支付', 'payUrl': Assets.icons.zhifubao.path} + ]; + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + Align( + child: SizedBox( + width: 750.w, + height: 1624.w, + ), + ), + Positioned( + child: Image.asset( + Assets.images.vipbg.path, + )), + Positioned(top: 256.w, left: 32.w, child: _getBanner()), + Positioned( + top: 480.w, + child: Container( + height: 1208.w, + width: 750.w, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20))), + child: Column(children: [ + _getRecharge(), + _getSpay(payWay), + PloneBottom( + border: true, + onTap: () {}, + textColor: const Color(0xFF333333), + text: "立即开通", + color1: const Color(0xFFFFF6D8), + color2: const Color(0xFFFFEAB0), + ) + ]), + )), + Positioned( + top: 68.w, + child: Row( + children: [ + const CloudBackButton( + isSpecial: true, + ), + 154.wb, + Text('会员中心', style: Theme.of(context).textTheme.headline6), + ], + )), + ], + )); + } + + // _get() { + // return Stack( + // children: [ + // Align( + // child: SizedBox( + // width: 750.w, + // height: 1410.w, + // ), + // ), + // Positioned( + // child: _getBanner(), + // ), + // Positioned( + // top: 224.w, + // child: Container( + // height: 1140.w, + // width: 750.w, + // decoration: const BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(20), + // topRight: Radius.circular(20))), + // child: Column(children: []), + // )) + // ], + // ); + // } + + //banner + _getBanner() { + return SizedBox( + // margin: EdgeInsets.symmetric(horizontal: 32.w), + child: Stack(children: [ + Align( + child: SizedBox( + width: 686.w, + child: Image.asset( + Assets.images.vipbanner.path, + width: 622.w, + height: 244.w, + fit: BoxFit.fill, + ), + )), + Positioned( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 64.w, vertical: 16.w), + child: _getText())) + ]), + ); + } + +//banner 文字 + _getText() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _getVip(), + 18.hb, + Text( + "解锁全部功能", + style: TextStyle( + color: Colors.white, + fontSize: BaseStyle.fontSize32, + fontWeight: FontWeight.bold), + ), + 16.hb, + Text( + "解锁全部功能", + style: TextStyle(color: Colors.white, fontSize: BaseStyle.fontSize24), + ) + ], + ); + } + + _getVip() { + return Row( + children: [ + const Text( + "vip", + style: TextStyle(color: Color(0xFFFFEAB0)), + ), + Container( + width: 8.w, + height: 8.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: const Color(0xFFFFEAB0), + ), + ), + const Text( + "未开通会员", + style: TextStyle(color: Color(0xFFFFEAB0)), + ) + ], + ); + } + +//充值金额 + _getRecharge() { + return Container( + width: double.infinity, + decoration: const BoxDecoration(), + clipBehavior: Clip.antiAlias, + child: ListView( + shrinkWrap: true, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 80.w, vertical: 32.w), + child: SortWidget( + crossAxisSpacing: 24.w, + itemList: _piceList, + childAspectRatio: 288 / 216, + crossAxisCount: 2, + mainAxisSpacing: 20.w, + callback: (item, index) { + _chooseItem = item; + // setState(() {}); + }, + pickItem: _chooseItem, + ), + ), + // 762.hb, + //40.hb, + ], + ), + ); + } + + _getSpay(List item) { + return Container( + // color: Colors.red, + width: double.infinity, + height: 500.w, + margin: EdgeInsets.symmetric(horizontal: 64.w), + child: ListView.builder( + itemBuilder: (context, index) { + return GestureDetector( + onTap: () { + _selectIndex = index; + // setState(() {}); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 40.w), + color: Colors.white, + child: Row( + children: [ + SizedBox( + child: Image.asset( + item[index]['payUrl'], + width: 40.w, + height: 40.h, + ), + ), + 20.wb, + SizedBox( + width: 200.w, + child: Text( + item[index]['payName'], + style: TextStyle( + color: BaseStyle.color333333, + fontSize: BaseStyle.fontSize28), + ), + ), + const Spacer(), + BeeCheckRadio( + value: index, + groupValue: [_selectIndex], + ), + ], + )), + ); + }, + itemCount: item.length, + ), + ); + + // ListView.builder( + // itemBuilder: (context, index) { + // return GestureDetector( + // onTap: () { + // _selectIndex = index; + // // setState(() {}); + // }, + // child: Container( + // padding: EdgeInsets.symmetric(horizontal: 24.w, vertical: 40.w), + // color: Colors.white, + // child: Row( + // children: [ + // SizedBox( + // child: Image.asset( + // item[index]['payUrl'], + // width: 40.w, + // height: 40.h, + // ), + // ), + // 20.wb, + // SizedBox( + // width: 200.w, + // child: Text( + // item[index]['payName'], + // style: TextStyle( + // color: BaseStyle.color333333, + // fontSize: BaseStyle.fontSize28), + // ), + // ), + // const Spacer(), + // BeeCheckRadio( + // value: index, + // groupValue: [_selectIndex], + // ), + // ], + // )), + // ); + // }, + // itemCount: item.length, + // ); + } +} diff --git a/lib/ui/user/user_page.dart b/lib/ui/user/user_page.dart new file mode 100644 index 0000000..6bc9757 --- /dev/null +++ b/lib/ui/user/user_page.dart @@ -0,0 +1,203 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:project_telephony/base/base_style.dart'; +import 'package:project_telephony/ui/home/home_page.dart'; +import 'package:project_telephony/ui/user/members_page.dart'; +import 'package:project_telephony/ui/widget/image_scaffold.dart'; +import 'package:project_telephony/ui/widget/plone_bottom.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class UserPage extends StatefulWidget { + const UserPage({Key? key}) : super(key: key); + + @override + _UserPageState createState() => _UserPageState(); +} + +class _UserPageState extends State { + bool bl = true; + @override + Widget build(BuildContext context) { + return CloudScaffold( + systemStyle: const SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + // systemNavigationBarColor: Colors.white, + ), + path: Assets.images.bg.path, + bodyColor: Colors.white, + extendBody: true, + body: Column(children: [ + _getUser(), + 36.hb, + _getBanner(), + 120.hb, + _getSwitch(Assets.icons.switch1.path, "功能开关", true), + _getSwitch(Assets.icons.privacy.path, "隐私政策", false), + _getSwitch(Assets.icons.permissions.path, "权限说明", false), + // const Spacer(), + PloneBottom( + border: false, + onTap: () { + Get.to(() => const HomePage()); + }, + textColor: const Color(0xFF1890FF), + color1: const Color(0xFFEBF5FF), + color2: const Color(0xFFEBF5FF), + text: "退出登录", + ) + ]), + ); + } + + //头像 + _getUser() { + return Container( + margin: EdgeInsets.only(left: 32.w, right: 32.w, top: 199.w), + child: Row( + children: [ + Column( + children: [ + Text( + bl ? "登录/注册" : "xxxxx", + style: TextStyle( + fontSize: BaseStyle.fontSize48, + color: BaseStyle.color333333, + fontWeight: FontWeight.bold), + ), + 24.hb, + Text( + bl ? "登录获取更多信息" : "欢迎您登录短信帮手", + style: TextStyle( + fontSize: BaseStyle.fontSize28, + color: BaseStyle.color333333), + ) + ], + ), + const Spacer(), + Container( + child: ClipOval( + child: Image.asset( + Assets.images.portrait.path, + height: 128.w, + width: 128.w, + fit: BoxFit.cover, + ), + ), + ) + ], + ), + ); + } + +//banner + _getBanner() { + return SizedBox( + // margin: EdgeInsets.symmetric(horizontal: 32.w), + height: 144.w, + child: Stack(children: [ + Align( + child: SizedBox( + width: 686.w, + child: Image.asset( + Assets.images.banner.path, + fit: BoxFit.fill, + ), + )), + Positioned( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 64.w, vertical: 16.w), + child: Row( + children: [_getText(), const Spacer(), _getBotton()], + ), + )) + ]), + ); + } + +//banner botton + _getBotton() { + return GestureDetector( + onTap: () { + Get.to(() => MembersPage()); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 32.w, vertical: 16.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + gradient: const LinearGradient( + colors: [Color(0xFFFFF6D8), Color(0xFFFFEAB0)], + begin: Alignment.centerLeft, + end: Alignment.centerRight)), + child: Text( + "立即开通", + style: TextStyle( + color: const Color(0xFF001F3F), fontSize: BaseStyle.fontSize24), + )), + ); + } + +//banner 文字 + _getText() { + return Column( + children: [ + _getVip(), + 16.hb, + Text( + "解锁全部功能", + style: TextStyle(color: Colors.white, fontSize: BaseStyle.fontSize24), + ) + ], + ); + } + + _getVip() { + return Row( + children: [ + const Text( + "vip", + style: TextStyle(color: Color(0xFFFFEAB0)), + ), + Container( + width: 8.w, + height: 8.w, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: const Color(0xFFFFEAB0), + ), + ), + const Text( + "未开通会员", + style: TextStyle(color: Color(0xFFFFEAB0)), + ) + ], + ); + } + +//内容 + _getSwitch(String url, String name, bool pd) { + bool vle = false; + return ListTile( + // onTap: (() {}), + leading: Image.asset( + url, + height: 54.w, + width: 56.w, + fit: BoxFit.fill, + ), + title: Text( + name, + style: TextStyle( + color: BaseStyle.color333333, + fontSize: BaseStyle.fontSize34, + fontWeight: FontWeight.bold), + ), + trailing: pd + ? Switch( + value: vle, + onChanged: (value) { + vle = value; + // setState(() {}); + }) + : const Icon(Icons.keyboard_arrow_right)); + } +} diff --git a/lib/ui/widget/check_radio.dart b/lib/ui/widget/check_radio.dart new file mode 100644 index 0000000..d4151c7 --- /dev/null +++ b/lib/ui/widget/check_radio.dart @@ -0,0 +1,68 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:project_telephony/base/base_style.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class BeeCheckRadio extends StatefulWidget { + final T? value; + final List? groupValue; + final Widget? indent; + final Color? backColor; + final bool? border; + const BeeCheckRadio({ + Key? key, + this.value, + this.groupValue, + this.indent, + this.backColor, + this.border = true, + }); + + @override + _BeeCheckRadioState createState() => _BeeCheckRadioState(); +} + +class _BeeCheckRadioState extends State { + bool get _selected { + if (widget.groupValue!.contains(widget.value)) { + return true; + } else { + return false; + } + } + + @override + Widget build(BuildContext context) { + return AnimatedContainer( + height: 40.w, + width: 40.w, + decoration: widget.border! + ? BoxDecoration( + color: widget.backColor ?? + kPrimaryColor.withOpacity(_selected ? 1 : 0), + border: Border.all( + color: widget.backColor != null + ? kForeGroundColor + : (_selected ? kPrimaryColor : const Color(0xFFcccccc)), + width: 2.w, + ), + borderRadius: BorderRadius.circular(20.w), + ) + : const BoxDecoration(), + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOutCubic, + alignment: Alignment.center, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOutCubic, + opacity: _selected ? 1 : 0, + child: widget.indent ?? + Icon( + CupertinoIcons.checkmark, + color: widget.border! ? Colors.white : const Color(0xFF027AFF), + size: 28.w, + ), + ), + ); + } +} diff --git a/lib/ui/widget/image_scaffold.dart b/lib/ui/widget/image_scaffold.dart new file mode 100644 index 0000000..6b0ea28 --- /dev/null +++ b/lib/ui/widget/image_scaffold.dart @@ -0,0 +1,142 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class CloudScaffold extends StatelessWidget { + ///沉浸式带背景的APPBar + ///path未背景图的path + final Widget? body; + final Widget? appbar; + final Color bodyColor; + final Widget? bottomNavi; + final FloatingActionButton? fab; + final bool extendBody; + final String? path; + final SystemUiOverlayStyle systemStyle; + final bool normal; + final String? title; + final List actions; + final Color? appBarBackColor; + final PreferredSizeWidget? appBarBottom; + final Widget? endDrawer; + + const CloudScaffold({ + Key? key, + this.body, + this.appbar, + this.bodyColor = const Color(0xFFF9F9F9), + this.bottomNavi, + this.fab, + // this.systemStyle = SystemStyle.initial, + this.extendBody = false, + this.path, + this.endDrawer, + required this.systemStyle, + }) : normal = false, + title = '', + actions = const [], + appBarBackColor = Colors.white, + appBarBottom = null; + + const CloudScaffold.white({ + Key? key, + this.body, + this.bottomNavi, + this.fab, + // this.systemStyle = SystemStyle.initial, + this.extendBody = false, + this.appbar, + this.path, + this.endDrawer, + required this.systemStyle, + }) : bodyColor = Colors.white, + normal = false, + title = '', + actions = const [], + appBarBackColor = Colors.white, + appBarBottom = null; + + const CloudScaffold.normal( + {Key? key, + this.body, + this.appbar, + this.bodyColor = const Color(0xFFF9F9F9), + this.bottomNavi, + this.fab, + // this.systemStyle = SystemStyle.initial, + this.extendBody = false, + this.path, + this.title, + this.appBarBackColor = Colors.white, + this.appBarBottom, + this.actions = const [], + this.endDrawer, + required this.systemStyle}) + : normal = true, + assert(title != null || appbar != null); + + @override + Widget build(BuildContext context) { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light); + return !normal + ? AnnotatedRegion( + value: systemStyle, + child: Scaffold( + endDrawer: endDrawer, + backgroundColor: bodyColor, + extendBodyBehindAppBar: extendBody, + extendBody: extendBody, + body: Stack( + children: [ + Positioned( + child: Image.asset( + path != null ? (path!) : Assets.images.homeBg.path, + width: double.infinity, + fit: BoxFit.fitWidth, + )), + Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + appbar ?? const SizedBox(), + body ?? const SizedBox(), + ], + ) + ], + ), + bottomNavigationBar: bottomNavi, + floatingActionButton: fab, + ), + ) + : AnnotatedRegion( + value: systemStyle, + child: Scaffold( + endDrawer: endDrawer, + backgroundColor: bodyColor, + extendBodyBehindAppBar: extendBody, + extendBody: extendBody, + body: body, + appBar: PreferredSize( + preferredSize: Size.fromHeight(176.w), + child: title == null + ? appbar! + : AppBar( + bottom: appBarBottom, + backgroundColor: + extendBody ? Colors.transparent : appBarBackColor, + // leading: const CloudBackButton(), + title: Text( + title!, + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: 32.sp), + ), + actions: actions, + ), + ), + bottomNavigationBar: bottomNavi, + floatingActionButton: fab, + ), + ); + } +} diff --git a/lib/ui/widget/plone_avatar_widget.dart b/lib/ui/widget/plone_avatar_widget.dart new file mode 100644 index 0000000..2ba0b8c --- /dev/null +++ b/lib/ui/widget/plone_avatar_widget.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +class PloneAvatarWidget extends StatelessWidget { + final List? urls; + final double? width; + final double? height; + final BoxFit? fit; + const PloneAvatarWidget({Key? key, this.width, + this.height, + this.urls, + this.fit = BoxFit.cover}) : super(key: key); + // String get imagePath { + // if (urls == null) { + // return ''; + // } else if (urls!.isEmpty) { + // return ''; + // } else { + // return urls!.first.imageWithHost; + // } + // } + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/ui/widget/plone_back_button.dart b/lib/ui/widget/plone_back_button.dart new file mode 100644 index 0000000..381aa63 --- /dev/null +++ b/lib/ui/widget/plone_back_button.dart @@ -0,0 +1,30 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:project_telephony/utils/headers.dart'; + +class CloudBackButton extends StatelessWidget { + final Color color; + final bool isSpecial; + + const CloudBackButton({ + Key? key, + this.color = const Color(0xFF111111), + this.isSpecial = false, + }); + + @override + Widget build(BuildContext context) { + return Navigator.canPop(context) + ? Padding( + padding: isSpecial ? EdgeInsets.only(left: 8.w) : EdgeInsets.zero, + child: IconButton( + onPressed: () => Get.back(), + icon: Icon( + CupertinoIcons.chevron_back, + color: color, + ), + ), + ) + : const SizedBox(); + } +} diff --git a/lib/ui/widget/plone_bottom.dart b/lib/ui/widget/plone_bottom.dart new file mode 100644 index 0000000..cf794b6 --- /dev/null +++ b/lib/ui/widget/plone_bottom.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +import '../../base/base_style.dart'; + +class PloneBottom extends StatefulWidget { + final String text; + final Color color1; + final Color color2; + final Color textColor; + final Function() onTap; + final bool blM; //是否间距 + final bool border; //是否有边框 + const PloneBottom({ + Key? key, + this.text = '返回首页', + this.color1 = const Color(0xFF0593FF), + this.color2 = const Color(0xFF027AFF), + this.textColor = kForeGroundColor, + this.blM = true, + this.border = false, + required this.onTap, + }); + + @override + State createState() => _PloneBottomState(); +} + +class _PloneBottomState extends State { + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onTap, + child: Material( + color: Colors.transparent, + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.symmetric(vertical: 15.w), + margin: widget.blM + ? EdgeInsets.symmetric(horizontal: 32.w) + : EdgeInsets.only(left: 0.w), + decoration: BoxDecoration( + border: Border.all( + color: const Color(0xFF1890FF), + width: !widget.border ? 1.w : 10.w), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [widget.color1, widget.color2]), + borderRadius: BorderRadius.circular(8.w)), + child: Text( + widget.text, + style: TextStyle( + fontSize: BaseStyle.fontSize28, color: widget.textColor), + ), + ), + ), + ); + } +} diff --git a/lib/ui/widget/putup_widget.dart b/lib/ui/widget/putup_widget.dart new file mode 100644 index 0000000..4956e42 --- /dev/null +++ b/lib/ui/widget/putup_widget.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:project_telephony/base/base_style.dart'; +import 'package:project_telephony/utils/headers.dart'; + +typedef ItemCallback = Function(ChooseItems item, int index); + +class ChooseItems { + int month; + int pice; + bool isChoose; + + ChooseItems({ + required this.month, + required this.pice, + this.isChoose = false, + }); +} + +class SortWidget extends StatelessWidget { + final List itemList; + final ItemCallback callback; + final int crossAxisCount; + final double mainAxisSpacing; + final double crossAxisSpacing; + final double childAspectRatio; + final bool haveButton; + final ChooseItems? pickItem; + + const SortWidget( + {Key? key, + required this.itemList, + required this.callback, + required this.crossAxisCount, + required this.mainAxisSpacing, + required this.crossAxisSpacing, + required this.childAspectRatio, + this.haveButton = false, + required this.pickItem}); + + @override + Widget build(BuildContext context) { + return GridView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: itemList.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + //横轴元素个数 + crossAxisCount: crossAxisCount, + //纵轴间距 + mainAxisSpacing: mainAxisSpacing, + //横轴间距 + crossAxisSpacing: crossAxisSpacing, + //子组件宽高长度比例 + childAspectRatio: childAspectRatio), + itemBuilder: (BuildContext context, int index) { + return _getItem(itemList[index], index); + }); + } + + _getItem(ChooseItems item, int index) { + return GestureDetector( + onTap: () { + callback(item, index); + }, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: pickItem == item + ? const Color(0xFFFFFAEA) + : const Color(0xFFFFFFFF), + borderRadius: BorderRadius.circular(4.w), + border: pickItem != item + ? Border.all(color: const Color(0xFFF5F5F5), width: 1.w) + : Border.all(color: const Color(0xFFFFDF66), width: 1.w)), + child: Column( + children: [ + Padding(padding: EdgeInsets.only(top: 16.w)), + Text( + "${item.month}个月VIP", + style: TextStyle( + fontSize: BaseStyle.fontSize28, + color: BaseStyle.color333333, + fontWeight: FontWeight.bold), + ), + Text.rich(TextSpan(children: [ + TextSpan( + text: "¥", + style: TextStyle( + fontSize: BaseStyle.fontSize32, + color: pickItem == item + ? const Color(0xFFFF3F3F) + : BaseStyle.color333333)), + TextSpan( + text: "${item.pice}", + style: TextStyle( + fontSize: 64.sp, + color: pickItem == item + ? const Color(0xFFFF3F3F) + : BaseStyle.color333333)) + ])), + Text( + "${(item.pice / item.month).toStringAsFixed(2)}元/月", + style: TextStyle( + fontSize: BaseStyle.fontSize24, + color: BaseStyle.color999999, + fontWeight: FontWeight.bold), + ), + ], + )), + ); + } +} diff --git a/lib/utils/headers.dart b/lib/utils/headers.dart new file mode 100644 index 0000000..20e13af --- /dev/null +++ b/lib/utils/headers.dart @@ -0,0 +1,7 @@ +// export 'package:cloud_car/base/base_style.dart'; +export 'package:project_telephony/extensions/num_ext.dart'; +export 'package:project_telephony/extensions/num_ext.dart'; +export 'package:project_telephony/extensions/wigget_list_ext.dart'; +export 'package:project_telephony/gen/assets.gen.dart'; +export 'package:flutter_screenutil/flutter_screenutil.dart'; +export 'package:get/get.dart'; diff --git a/pubspec.lock b/pubspec.lock index 2ba9110..8b820aa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "40.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.1" async: dependency: transitive description: @@ -15,6 +36,69 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + bot_toast: + dependency: "direct main" + description: + name: bot_toast + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.3" + build: + dependency: transitive + description: + name: build + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.0" + build_config: + dependency: transitive + description: + name: build_config + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + build_runner: + dependency: "direct dev" + description: + name: build_runner + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + url: "https://pub.dartlang.org" + source: hosted + version: "7.2.3" + built_collection: + dependency: transitive + description: + name: built_collection + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + url: "https://pub.dartlang.org" + source: hosted + version: "8.4.0" call_log: dependency: "direct main" description: @@ -36,6 +120,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" clock: dependency: transitive description: @@ -43,6 +134,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + code_builder: + dependency: transitive + description: + name: code_builder + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" collection: dependency: transitive description: @@ -50,55 +148,55 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" - cupertino_icons: - dependency: "direct main" + color: + dependency: transitive description: - name: cupertino_icons + name: color url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" - device_info_plus: - dependency: "direct main" + version: "3.0.0" + convert: + dependency: transitive description: - name: device_info_plus + name: convert url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" - device_info_plus_linux: + version: "3.0.2" + crypto: dependency: transitive description: - name: device_info_plus_linux + name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" - device_info_plus_macos: - dependency: transitive + version: "3.0.2" + cupertino_icons: + dependency: "direct main" description: - name: device_info_plus_macos + name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "2.2.3" - device_info_plus_platform_interface: + version: "1.0.5" + dart_style: dependency: transitive description: - name: device_info_plus_platform_interface + name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.3.0+1" - device_info_plus_web: + version: "2.2.3" + dartx: dependency: transitive description: - name: device_info_plus_web + name: dartx url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" - device_info_plus_windows: - dependency: transitive + version: "1.1.0" + device_info_plus: + dependency: "direct main" description: - name: device_info_plus_windows + name: device_info_plus url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "0.0.1" fake_async: dependency: transitive description: @@ -120,6 +218,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.1.2" + fixnum: + dependency: transitive + description: + name: fixnum + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" flutter: dependency: "direct main" description: flutter @@ -131,7 +236,42 @@ packages: name: flutter_background_service url: "https://pub.dartlang.org" source: hosted - version: "0.2.6" + version: "0.2.8+5" + flutter_background_service_android: + dependency: transitive + description: + name: flutter_background_service_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + flutter_background_service_ios: + dependency: transitive + description: + name: flutter_background_service_ios + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.2" + flutter_background_service_platform_interface: + dependency: transitive + description: + name: flutter_background_service_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+3" + flutter_gen_core: + dependency: transitive + description: + name: flutter_gen_core + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.0" + flutter_gen_runner: + dependency: "direct dev" + description: + name: flutter_gen_runner + url: "https://pub.dartlang.org" + source: hosted + version: "4.3.0" flutter_lints: dependency: "direct dev" description: @@ -139,23 +279,102 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + flutter_screenutil: + dependency: "direct main" + description: + name: flutter_screenutil + url: "https://pub.dartlang.org" + source: hosted + version: "5.5.3+2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" - flutter_web_plugins: + frontend_server_client: dependency: transitive - description: flutter - source: sdk - version: "0.0.0" + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + get: + dependency: "direct main" + description: + name: get + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.5" + glob: + dependency: transitive + description: + name: glob + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + graphs: + dependency: transitive + description: + name: graphs + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + hive: + dependency: "direct main" + description: + name: hive + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.3" + hive_flutter: + dependency: "direct main" + description: + name: hive_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + url: "https://pub.dartlang.org" + source: hosted + version: "4.0.1" + io: + dependency: transitive + description: + name: io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" js: dependency: transitive description: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "4.6.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + url: "https://pub.dartlang.org" + source: hosted + version: "6.3.1" lints: dependency: transitive description: @@ -163,6 +382,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + lpinyin: + dependency: "direct main" + description: + name: lpinyin + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.3" matcher: dependency: transitive description: @@ -170,6 +403,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -177,6 +417,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.7.0" + mime: + dependency: transitive + description: + name: mime + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" nested: dependency: transitive description: @@ -184,6 +431,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + package_info: + dependency: "direct main" + description: + name: package_info + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.2" path: dependency: transitive description: @@ -191,6 +452,62 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_provider: + dependency: transitive + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.17" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.7" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.6" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" platform: dependency: transitive description: @@ -205,6 +522,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + pool: + dependency: transitive + description: + name: pool + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" provider: dependency: "direct main" description: @@ -212,11 +543,53 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "6.0.3" + pub_semver: + dependency: "direct dev" + description: + name: pub_semver + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + shelf: + dependency: transitive + description: + name: shelf + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.2" + source_helper: + dependency: transitive + description: + name: source_helper + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.2" source_span: dependency: transitive description: @@ -238,6 +611,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" string_scanner: dependency: transitive description: @@ -251,7 +631,7 @@ packages: name: telephony url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.2.0" term_glyph: dependency: transitive description: @@ -265,7 +645,21 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.8" + time: + dependency: transitive + description: + name: time + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + timing: + dependency: transitive + description: + name: timing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" typed_data: dependency: transitive description: @@ -280,6 +674,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + watcher: + dependency: transitive + description: + name: watcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" win32: dependency: transitive description: @@ -287,6 +695,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.5.2" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0+1" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.1" + yaml: + dependency: "direct dev" + description: + name: yaml + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.1" sdks: - dart: ">=2.15.0 <3.0.0" - flutter: ">=2.0.0" + dart: ">=2.16.1 <3.0.0" + flutter: ">=2.8.1" diff --git a/pubspec.yaml b/pubspec.yaml index 767fc80..2fb8b06 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,11 +34,31 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 +# 获取来电状态 telephony: any provider: ^6.0.3 +# 获取通话记录 call_log: ^4.0.0 - flutter_background_service: any +# 后台设置 + flutter_background_service: ^0.2.6 device_info_plus: any +# 屏幕适配 + flutter_screenutil: ^5.5.3+2 +# 路由相关 + get: ^4.6.5 +# 包的信息 + package_info: ^2.0.0 +# 本地化存储 + hive: ^2.0.4 + hive_flutter: ^1.1.0 +# 弹出框 + bot_toast: ^4.0.3 +# 汉转音 + lpinyin: ^2.0.3 +# # jdk +# jverify: ^2.2.5 +## pub 集成 +# jpush_flutter: 2.1.4 dev_dependencies: flutter_test: @@ -50,45 +70,20 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^1.0.0 + #model自动生成 + json_serializable: ^6.1.3 + build_runner: ^2.0.2 + yaml: ^3.1.0 + pub_semver: ^2.1.0 + flutter_gen_runner: ^4.1.3 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec -# The following section is specific to Flutter. flutter: + uses-material-design: true + generate: true + assets: + - assets/ + - assets/icons/ + - assets/images/ +# - assets/data/ - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages