diff --git a/lib/class/class_account.dart b/lib/class/class_account.dart index d1519bca..9ce7e502 100644 --- a/lib/class/class_account.dart +++ b/lib/class/class_account.dart @@ -9,6 +9,7 @@ class Account { String? accountHolder; String? accountAlias; + Account () { ModelUuid uuid = ModelUuid(); accountId = uuid.randomId; @@ -30,7 +31,7 @@ class Account { 'accountalias' : accountAlias, }; - void creatAccount(String userid) async { + void createAccount(String userid) async { await FirebaseFirestore.instance.collection("accountlist").doc(accountId).set(toJson()); } diff --git a/lib/main.dart b/lib/main.dart index 0f65c1fb..46d57ad9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:groupsettlement2/view/account_add_page.dart'; +import 'package:groupsettlement2/view/account_detail_page.dart'; import 'package:groupsettlement2/view/group_create_page.dart'; import 'package:groupsettlement2/view/group_main_page.dart'; import 'package:groupsettlement2/view/group_select_page.dart'; @@ -192,6 +193,11 @@ final GoRouter _router = GoRouter( builder: (context,state) { return AccountAddPage(); } + ), + GoRoute(path: 'AccountDetail', + builder: (context,state) { + return AccountDetailPage(); + } ) ] ), diff --git a/lib/view/account_add_page.dart b/lib/view/account_add_page.dart index 5c33caa7..e0ff4541 100644 --- a/lib/view/account_add_page.dart +++ b/lib/view/account_add_page.dart @@ -2,25 +2,32 @@ import 'dart:math'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:groupsettlement2/view/shared_basic_widget.dart'; +import 'package:groupsettlement2/viewmodel/UserViewModel.dart'; +import '../class/class_account.dart'; import '../design_element.dart'; -class AccountAddPage extends StatefulWidget { +class AccountAddPage extends ConsumerStatefulWidget { const AccountAddPage({Key? key}) : super(key: key); @override - State createState() => _AccountAddPageState(); + ConsumerState createState() => _AccountAddPageState(); } -class _AccountAddPageState extends State { +class _AccountAddPageState extends ConsumerState { final List _banks = ["하나은행","국민은행","외환은행","기업은행","부산은행","신한은행","대구은행"]; String? bank; + String accountNum = ""; String ownerName = ""; String accountNickname = ""; bool _makeFavorite = false; @override Widget build(BuildContext context) { final Size size = MediaQuery.of(context).size; - String accountNum = ""; String ownerName = ""; + + final provider = ref.watch(userProvider); + return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar( @@ -81,7 +88,23 @@ class _AccountAddPageState extends State { color: Colors.black, ), ), - SizedBox(height:size.height*0.12), + SizedBox(height:size.height*0.06), + Text("계좌 별명을 입력해주세요.", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + ), + TextField( + decoration: InputDecoration( + hintText: "계좌 별명", + ), + onChanged: (val){ + accountNickname = val; + }, + ), + SizedBox(height:20), Text("은행사를 선택해주세요.", style: TextStyle( fontSize: 18, @@ -90,10 +113,9 @@ class _AccountAddPageState extends State { ), ), Container( - width: size.width*0.9, + width: size.width*0.4, child: DropdownButton( isExpanded: true, - value: bank, items: _banks.map((e)=> DropdownMenuItem(child: Text(e),value: e,)).toList(), @@ -115,6 +137,13 @@ class _AccountAddPageState extends State { ), ), TextField( + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + decoration: InputDecoration( + hintText: "계좌번호 ('-'기호를 제외하고 입력\)", + ), onChanged: (val){ accountNum = val; }, @@ -128,6 +157,10 @@ class _AccountAddPageState extends State { ), ), TextField( + controller: TextEditingController()..text = provider.userData.name!, + decoration: InputDecoration( + hintText: "예금주", + ), onChanged: (val){ ownerName = val; }, @@ -141,6 +174,9 @@ class _AccountAddPageState extends State { contentPadding: EdgeInsets.zero, dense : true, activeColor: Colors.black, + checkboxShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100) + ), onChanged: (val){ setState(() { _makeFavorite = !_makeFavorite; @@ -155,9 +191,31 @@ class _AccountAddPageState extends State { margin: EdgeInsets.only(bottom: 20), child: OutlinedButton( onPressed: (){ - print(bank); - print(ownerName); - print(accountNum); + Account account = Account(); + try{ + account.accountNum = int.parse(accountNum); + }catch(e){ + print(e); + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + '계좌번호에는 숫자만 넣어주세요.'), + duration: Duration(seconds: 2), + )); + return; + } + account.accountAlias = accountNickname; + account.accountHolder = ownerName; + account.bank = bank; + + provider.addAccount(account,_makeFavorite); + Navigator.of(context).pop(); + ScaffoldMessenger.of(context).clearSnackBars(); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text( + '성공적으로 계좌를 등록했어요.'), + duration: Duration(seconds: 2), + )); }, style: OutlinedButton.styleFrom( backgroundColor: Color(0xFF454545), diff --git a/lib/view/account_detail_page.dart b/lib/view/account_detail_page.dart new file mode 100644 index 00000000..3f17c201 --- /dev/null +++ b/lib/view/account_detail_page.dart @@ -0,0 +1,227 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:groupsettlement2/view/shared_basic_widget.dart'; +import 'package:groupsettlement2/viewmodel/UserViewModel.dart'; + +import '../class/class_account.dart'; +import '../design_element.dart'; + +class AccountDetailPage extends ConsumerStatefulWidget { + const AccountDetailPage({Key? key}) : super(key: key); + + @override + ConsumerState createState() => _AccountDetailPageState(); +} + +class _AccountDetailPageState extends ConsumerState { + int viewDetailIndex = -1; + @override + Widget build(BuildContext context) { + final Size size = MediaQuery.of(context).size; + final provider = ref.watch(userProvider); + print(provider.userData.accountInfo.length); + return Scaffold( + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left:20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(width:1,height:size.height*0.12), + Text("마이페이지", + style: TextStyle( + fontSize: 13, + fontWeight: FontWeight.w400, + ) + ), + SizedBox(height:size.height*0.02), + Text("나의 계좌 목록", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + ), + ) + ], + ), + ), + SizedBox(height:size.height*0.05), + Column( + children: List.generate(provider.accounts.length, (index){ + return GestureDetector( + onTap: (){ + setState(() { + viewDetailIndex = viewDetailIndex == index ? -1 : index; + }); + }, + child: oneAccount( + account: provider.accounts[index], + isFavorite: index == 0, + viewDetail: viewDetailIndex == index,)); + }) + ), + + ], + ), + + bottomNavigationBar: CustomBottomNavigationBar(index: 4,isIn: false), + ); + } +} + +class oneAccount extends ConsumerStatefulWidget { + final Account account; + final bool isFavorite; + final bool viewDetail; + const oneAccount({Key? key,required this.account,required this.isFavorite,required this.viewDetail}) : super(key: key); + + @override + ConsumerState createState() => _oneAccountState(); +} + +class _oneAccountState extends ConsumerState { + + @override + Widget build(BuildContext context) { + bool viewDetail = widget.viewDetail; + final Size size = MediaQuery.of(context).size; + double fontsize = widget.isFavorite ? 15 : 14; + double detailSize = widget.isFavorite ? size.height*0.23 : size.height*0.18; + double simpleSize = widget.isFavorite ? size.height*0.11 : size.height*0.08; + final provider = ref.watch(userProvider); + return AnimatedContainer( + curve: Curves.fastEaseInToSlowEaseOut, + duration: Duration(milliseconds: 500), + height: viewDetail ? detailSize : simpleSize, + child: SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + widget.isFavorite ? Text("대표 계좌", + style: TextStyle( + color: color1, + fontSize:20, + fontWeight: FontWeight.w600 + ), + ) : SizedBox.shrink(), + Text(widget.account.accountAlias!, + style: TextStyle( + fontSize: widget.isFavorite ? 20 : 17, + fontWeight: FontWeight.w600, + ), + ), + Row( + children: [ + Text(widget.account.bank!+" ", + style: TextStyle( + fontSize: fontsize, + fontWeight: FontWeight.w500, + ), + ), + viewDetail ? SizedBox.shrink(): Text(widget.account.accountNum!.toString(), + style: TextStyle( + fontSize: fontsize, + fontWeight: FontWeight.w500, + ), + ) + ], + ), + viewDetail ? Text(widget.account.accountNum!.toString(), + style: TextStyle( + fontSize: fontsize, + fontWeight: FontWeight.w500, + ), + ) : Divider(), + SizedBox(height:10), + + + ], + ), + ), + Container( + height: 80, + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.7), + ), + const BoxShadow( + color: Color(0xFFF4F4F4), + spreadRadius: -2.0, + blurRadius: 4.0, + ) + ] + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + width:size.width*0.44, + height: 55, + margin: EdgeInsets.only(left:15), + child: OutlinedButton( + onPressed: (){ + provider.accounts.remove(widget.account); + provider.accounts.insert(0,widget.account); + + }, + style: OutlinedButton.styleFrom( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + side: const BorderSide( + color: Color(0xFFD9D9D9), + ), + ), + child: Text("계좌 수정", + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + )) + ), + Container( + width:size.width*0.44, + height: 55, + margin: EdgeInsets.only(right:15), + child: OutlinedButton( + onPressed: (){ + setState(() { + provider.deleteAccount(widget.account, provider.accounts.indexOf(widget.account)); + }); + }, + style: OutlinedButton.styleFrom( + backgroundColor: color1, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + side: const BorderSide( + color: Colors.transparent + ), + ), + child: Text("계좌 삭제", + style: TextStyle( + color:Colors.white, + fontSize: 16, + fontWeight: FontWeight.w600, + ), + )) + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/view/group_create_page.dart b/lib/view/group_create_page.dart index f55e34e9..ddb46434 100644 --- a/lib/view/group_create_page.dart +++ b/lib/view/group_create_page.dart @@ -280,6 +280,7 @@ class _GroupCreatePageState extends ConsumerState { if (inputName != "") { provider.addByDirect(inputName); } else { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context) .showSnackBar( const SnackBar( @@ -376,6 +377,7 @@ class _GroupCreatePageState extends ConsumerState { child: GestureDetector( onTap: () { if (inputGroupName == "") { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('그룹명은 공백이 될 수 없습니다'), diff --git a/lib/view/group_main_page.dart b/lib/view/group_main_page.dart index 56c22f09..bf6fd2a5 100644 --- a/lib/view/group_main_page.dart +++ b/lib/view/group_main_page.dart @@ -25,11 +25,11 @@ class _GroupMainPageState extends ConsumerState { int i = 0; @override Widget build(BuildContext context) { - final gvm = ref.watch(groupProvider); + final provider = ref.watch(groupProvider); Size size = MediaQuery.of(context).size; if (isFirst) { Future(() { - gvm.settingGroupViewModel(widget.info[0], widget.info[1]); + provider.settingGroupViewModel(widget.info[0], widget.info[1]); }); ref .watch(bottomSheetSliderChangeNotifierProvider.notifier) @@ -41,7 +41,7 @@ class _GroupMainPageState extends ConsumerState { final bottomsheetValue = ref.watch(bottomSheetSliderChangeNotifierProvider); Map countMap = Map(); - gvm.settlementInGroup.forEach((settlement) { + provider.settlementInGroup.forEach((settlement) { if (countMap.containsKey(settlement.masterUserId)) { countMap[settlement.masterUserId!] = countMap[settlement.masterUserId!]! + 1; @@ -60,7 +60,7 @@ class _GroupMainPageState extends ConsumerState { } }); - gvm.serviceUsers.forEach((user) { + provider.serviceUsers.forEach((user) { if (user.serviceUserId == mostCommonName) commonStmName = user.name!; }); @@ -86,7 +86,7 @@ class _GroupMainPageState extends ConsumerState { ), Row(children: [ Text( - gvm.myGroup.groupName ?? "", + provider.myGroup.groupName ?? "", style: const TextStyle( color: Colors.black, fontSize: 30, @@ -112,8 +112,8 @@ class _GroupMainPageState extends ConsumerState { onPressed: () { Navigator.of(context).pop(); setState(() { - gvm.updateGroup( - gvm.myGroup.groupId!, inputName); + provider.updateGroup( + provider.myGroup.groupId!, inputName); }); }, style: OutlinedButton.styleFrom( @@ -179,7 +179,7 @@ class _GroupMainPageState extends ConsumerState { text: " ", ), TextSpan( - text: gvm.settlementInGroup.length.toString(), + text: provider.settlementInGroup.length.toString(), style: const TextStyle( color: Color(0xFFFE5F55), fontSize: 25, @@ -207,13 +207,13 @@ class _GroupMainPageState extends ConsumerState { SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( - children: List.generate(gvm.settlementInGroup.length, + children: List.generate(provider.settlementInGroup.length, (index) { - Settlement settlement = gvm.settlementInGroup[index]; + Settlement settlement = provider.settlementInGroup[index]; return StmItem( size: size, masterId: settlement.masterUserId!, - userId: gvm.userData.serviceUserId!, + userId: provider.userData.serviceUserId!, settlement: settlement); })), ), @@ -236,7 +236,7 @@ class _GroupMainPageState extends ConsumerState { TextSpan( children: [ TextSpan( - text: '${gvm.myGroup.groupName} 그룹에서 가장 많이 \n', + text: '${provider.myGroup.groupName} 그룹에서 가장 많이 \n', style: TextStyle( color: Colors.black, fontSize: 15, @@ -271,7 +271,8 @@ class _GroupMainPageState extends ConsumerState { SizedBox(height: 20), GestureDetector( onTap: () { - // context.push(); + context.push( + "/SettlementCreate", extra: [provider.myGroup, provider.userData]); }, child: Padding( padding: EdgeInsets.only(right: 15), @@ -349,7 +350,7 @@ class _GroupMainPageState extends ConsumerState { EdgeInsets.only(left: size.width * 0.65), child: Text.rich(TextSpan(children: [ TextSpan( - text: "${gvm.serviceUsers.length} ", + text: "${provider.serviceUsers.length} ", style: TextStyle( color: Color(0xFFFE5F55), fontSize: 20, @@ -418,7 +419,7 @@ class _GroupMainPageState extends ConsumerState { SizedBox(height: 20), Column( children: List.generate( - (gvm.serviceUsers.length / 4) + (provider.serviceUsers.length / 4) .toInt() + 1, (index) { return Row( @@ -427,7 +428,7 @@ class _GroupMainPageState extends ConsumerState { try { return oneUser( flag: false, - user: gvm.serviceUsers[ + user: provider.serviceUsers[ index * 4 + innerIndex]); } on RangeError catch (e) { @@ -517,9 +518,10 @@ class _GroupMainPageState extends ConsumerState { ServiceUser user = ServiceUser(); if (inputName != null) { - gvm.addByDirect( + provider.addByDirect( inputName!); } else { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of( context) .showSnackBar( diff --git a/lib/view/group_select_page.dart b/lib/view/group_select_page.dart index ee1bb241..a6ed73f9 100644 --- a/lib/view/group_select_page.dart +++ b/lib/view/group_select_page.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:groupsettlement2/class/class_user.dart'; import 'package:groupsettlement2/view/shared_basic_widget.dart'; import '../class/class_group.dart'; +import '../class/class_settlement.dart'; import '../design_element.dart'; import '../viewmodel/UserViewModel.dart'; @@ -115,13 +116,14 @@ class OneGroup extends ConsumerStatefulWidget { class _OneGroupState extends ConsumerState { @override Widget build(BuildContext context) { - var uvm = ref.watch(userProvider); + var provider = ref.watch(userProvider); + return Column( children: [ GestureDetector( onTap: () { context.push("/GroupSelect/GroupMain", - extra: [uvm.userData, widget.group.groupId!]); + extra: [provider.userData, widget.group.groupId!]); }, child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -167,15 +169,12 @@ class _OneGroupState extends ConsumerState { ), ], ), - GestureDetector( - onTap: () {}, - child: const Padding( - padding: EdgeInsets.only(bottom: 25), - child: Text("시간", - style: TextStyle( - color: Color(0xFF838383), - )), - ), + Padding( + padding: EdgeInsets.only(bottom: 25), + child: Text(provider.getGroupRecentActivityTime(widget.group), + style: TextStyle( + color: Color(0xFF838383), + )), ) ]), ), diff --git a/lib/view/group_settlement_list_page.dart b/lib/view/group_settlement_list_page.dart index 73cd7f3e..c0526120 100644 --- a/lib/view/group_settlement_list_page.dart +++ b/lib/view/group_settlement_list_page.dart @@ -352,6 +352,7 @@ class _GroupSettlementListPageState Navigator.of(context).pop(); } } else { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context) .showSnackBar(SnackBar( content: diff --git a/lib/view/main_page.dart b/lib/view/main_page.dart index 86b453e5..2b78523e 100644 --- a/lib/view/main_page.dart +++ b/lib/view/main_page.dart @@ -97,25 +97,22 @@ class _MainPage extends ConsumerState { height: 1, decoration: const BoxDecoration(color: Color(0xFFF4F4F4)), ), - Padding( - padding: const EdgeInsets.only(left: 0, right: 0), - child: Container( - width: double.infinity, - height: 200, - decoration: BoxDecoration(color: Colors.white, boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.7), - blurRadius: 7, - ) - ]), - child: const Center( - child: Text("문구 들어갈 위치", - style: TextStyle( - color: Colors.red, - fontWeight: FontWeight.w600, - fontSize: 15)), - )), - ), + Container( + width: double.infinity, + height: 200, + decoration: BoxDecoration(color: Colors.white, boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.7), + blurRadius: 7, + ) + ]), + child: const Center( + child: Text("문구 들어갈 위치", + style: TextStyle( + color: Colors.red, + fontWeight: FontWeight.w600, + fontSize: 15)), + )), Container( width: double.infinity, height: 160, @@ -581,8 +578,9 @@ class _SimpleSettlementerResState extends State { @override Widget build(BuildContext context) { - bool isRealNum = widget.res == widget.res.toInt(); + bool isInt = widget.res == widget.res.toInt(); double res = widget.res * 1; + Size size = MediaQuery.of(context).size; return SingleChildScrollView( scrollDirection: Axis.horizontal, physics: const NeverScrollableScrollPhysics(), @@ -591,72 +589,67 @@ class _SimpleSettlementerResState extends State { duration: Duration(milliseconds: widget.flag ? 700 : 300), width: widget.flag ? 1000 : 0, height: 100, - child: Stack( - children: [ - Positioned( - top: 10, - left: 20, - child: Text("1인당", - style: TextStyle( - fontWeight: FontWeight.w600, - )), - ), - Positioned( - top: 25, - left: 80, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.ideographic, - children: [ - AnimatedDigitWidget( - fractionDigits: 0, - duration: Duration(seconds: 1), - value: isRealNum ? res : res.toInt(), - enableSeparator: true, - suffix: isRealNum ? "원" : ".", - textStyle: TextStyle( - fontSize: 30, - fontWeight: FontWeight.w600, - color: color1, - ), - ), - isRealNum && (res * 100 % 100) / 10 < 1 ? SizedBox.shrink() : AnimatedDigitWidget( - value: 0, - textStyle: TextStyle( - textBaseline: TextBaseline.alphabetic, - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.grey, - ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(left:20,top:5), + child: Text("1인당", + style: TextStyle( + fontWeight: FontWeight.w600, + )), + ), + Container( + width: size.width*0.6, + margin: EdgeInsets.only(right:20), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.ideographic, + children: [ + AnimatedDigitWidget( + fractionDigits: 0, + duration: Duration(seconds: 1), + value: isInt ? res : res.toInt(), + enableSeparator: true, + suffix: isInt ? "원" : ".", + textStyle: TextStyle( + fontSize: 30, + fontWeight: FontWeight.w600, + color: color1, + ), + ), + (res * 10 % 10) > 1 || isInt ? SizedBox.shrink() : AnimatedDigitWidget( + value: 0, + textStyle: TextStyle( + textBaseline: TextBaseline.alphabetic, + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.grey, + ), + ), + isInt ? SizedBox.shrink() : AnimatedDigitWidget( + duration: Duration(seconds: 1), + value : res * 100 % 100, + enableSeparator: true, + suffix: "원", + textStyle: TextStyle( + textBaseline: TextBaseline.alphabetic, + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.grey, + ), + ) + ], ), - isRealNum ? SizedBox.shrink() : AnimatedDigitWidget( - duration: Duration(seconds: 1), - value : res * 100 % 100, - enableSeparator: true, - suffix: "원", - textStyle: TextStyle( - textBaseline: TextBaseline.alphabetic, - fontSize: 18, - fontWeight: FontWeight.w600, - color: Colors.grey, - ), - ) - ], - ) - // child: Text("${priceToString.format(widget.res)}원", - // textAlign: TextAlign.start, - // style: TextStyle( - // fontSize: 30, - // fontWeight: FontWeight.w600, - // color: color1, - // ) - // ), - ), - Positioned( - top: 13, - left: 265, - child: Stack( + ), + ], + ), + Stack( children: [ SizedBox( height: 60, @@ -687,9 +680,9 @@ class _SimpleSettlementerResState extends State { fontSize: 15)), ) ], - ), - ) - ], + ) + ], + ), )), ); } @@ -718,15 +711,15 @@ class _RecentSettlementState extends ConsumerState { @override Widget build(BuildContext context) { - var mvm = ref.watch(userProvider); + var provider = ref.watch(userProvider); bool masterFlag = - widget.settlement.masterUserId == mvm.userData.serviceUserId; - barSize = widget.size.width * 0.67; - currentMoney = mvm.getCurrentMoney(widget.settlement); - totalPrice = mvm.getTotalPrice(widget.settlement); + widget.settlement.masterUserId == provider.userData.serviceUserId; + barSize = widget.size.width * 0.64; + currentMoney = provider.getCurrentMoney(widget.settlement); + totalPrice = provider.getTotalPrice(widget.settlement); if (!masterFlag) { - sendMoney = mvm.getSendMoney(widget.settlement); - _didSend = widget.settlement.checkSent[mvm.userData.serviceUserId] == 2; + sendMoney = provider.getSendMoney(widget.settlement); + _didSend = widget.settlement.checkSent[provider.userData.serviceUserId] == 2; } dt = widget.settlement.time != null ? DateTime.parse(widget.settlement.time!.toDate().toString()) @@ -736,12 +729,12 @@ class _RecentSettlementState extends ConsumerState { GestureDetector( onTap: () { context.go( - "/SettlementInformation", extra: [widget.settlement, mvm.myGroup[mvm.myGroup.indexWhere((group) { + "/SettlementInformation", extra: [widget.settlement, provider.myGroup[provider.myGroup.indexWhere((group) { if (group.groupId == widget.settlement.groupId){ return true; } return false; - })], mvm.userData]); + })], provider.userData]); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -796,7 +789,7 @@ class _RecentSettlementState extends ConsumerState { ])), Padding( padding: const EdgeInsets.only(top: 20, left: 5), - child: Text(mvm.getGroupName(widget.settlement), + child: Text(provider.getGroupName(widget.settlement), style: TextStyle( color: Colors.grey, fontWeight: FontWeight.w600)), @@ -814,58 +807,63 @@ class _RecentSettlementState extends ConsumerState { child: Divider(), ), Row( - //mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: EdgeInsets.only(left: 10), - child: Text(masterFlag ? "받을 금액" : "보낼 금액", - style: TextStyle( - color: Colors.grey, - fontSize: 12, - fontWeight: FontWeight.w600)), - ), - Padding( - padding: EdgeInsets.only(left: 10), - child: Text( - masterFlag - ? priceToString.format(currentMoney) - : _didSend - ? "송금 완료" - : priceToString.format(sendMoney), - style: TextStyle( - color: masterFlag ? color1 : color2, - fontWeight: FontWeight.w600, - fontSize: 20)), - ), - Text( - masterFlag - ? " / ${priceToString.format(totalPrice)}" - : "", - style: TextStyle( - color: Colors.grey, - fontSize: 15, - fontWeight: FontWeight.w800)), - Padding( - padding: const EdgeInsets.only(left: 35), - child: Text( - masterFlag - ? currentMoney == totalPrice - ? "정산이 완료되었습니다" - : "" - : widget.settlement.checkSent[mvm - .userData.serviceUserId] == - 2 - ? "" - : "항목을 눌러 송금 확인하기", - //정산 완료 확인 체크필요 - style: TextStyle( - color: masterFlag ? color1 : color2, - fontWeight: FontWeight.w600, - fontSize: 13, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + //mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: EdgeInsets.only(left: 10), + child: Text(masterFlag ? "받을 금액" : "보낼 금액", + style: TextStyle( + color: Colors.grey, + fontSize: 12, + fontWeight: FontWeight.w600)), ), + Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + masterFlag + ? priceToString.format(currentMoney) + : _didSend + ? "송금 완료" + : priceToString.format(sendMoney), + style: TextStyle( + color: masterFlag ? color1 : color2, + fontWeight: FontWeight.w600, + fontSize: 20)), + ), + Text( + masterFlag + ? " / ${priceToString.format(totalPrice)}" + : "", + style: TextStyle( + color: Colors.grey, + fontSize: 15, + fontWeight: FontWeight.w800)), + ]), + Padding( + padding: const EdgeInsets.only(right:10), + child: Text( + masterFlag + ? currentMoney == totalPrice + ? "정산이 완료되었습니다" + : "" + : widget.settlement.checkSent[provider + .userData.serviceUserId] == + 2 + ? "" + : "항목을 눌러 송금 확인하기", + //정산 완료 확인 체크필요 + style: TextStyle( + color: masterFlag ? color1 : color2, + fontWeight: FontWeight.w600, + fontSize: 13, ), - ) - ]), + ), + ) + ], + ), Padding( padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(), diff --git a/lib/view/mypage_page.dart b/lib/view/mypage_page.dart index 623756b6..b1bd6742 100644 --- a/lib/view/mypage_page.dart +++ b/lib/view/mypage_page.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:groupsettlement2/view/shared_basic_widget.dart'; import 'package:flutter/services.dart'; +import '../class/class_account.dart'; import '../design_element.dart'; import '../viewmodel/UserViewModel.dart'; class MyPage extends ConsumerStatefulWidget { @@ -94,10 +95,11 @@ class _MyPageState extends ConsumerState { child: OutlinedButton( onPressed: () { if(inputName == ""){ + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text( '이름은 공백이 될 수 없습니다.'), - duration: Duration(seconds: 3), + duration: Duration(milliseconds: 1000), )); return; } @@ -105,10 +107,11 @@ class _MyPageState extends ConsumerState { setState(() { uvm.editUsername(inputName); }); + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text( '성공적으로 이름을 변경했습니다.'), - duration: Duration(seconds: 3), + duration: Duration(milliseconds: 1000), )); }, style: OutlinedButton.styleFrom( @@ -189,7 +192,9 @@ class _MyPageState extends ConsumerState { ) ), GestureDetector( - onTap:(){}, + onTap:(){ + context.push("/MyPage/AccountDetail"); + }, child:const Text( "자세히 보기>", style: TextStyle( @@ -212,20 +217,21 @@ class _MyPageState extends ConsumerState { children: [ GestureDetector( onTap: ()async{ - await Clipboard.setData(ClipboardData(text: uvm.userData.accountInfo.first)); + await Clipboard.setData(ClipboardData(text: uvm.accounts.first.accountNum.toString())); + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: const Center( child: Text( '계좌번호가 복사되었습니다.'), ), - duration: const Duration(milliseconds: 1500), + duration: const Duration(milliseconds: 1000), width: size.width*0.5, backgroundColor: color1, shape: StadiumBorder(), behavior: SnackBarBehavior.floating, )); }, - child: Text(uvm.userData.accountInfo.first, + child: Text(uvm.accounts.first.bank!+" "+uvm.accounts.first.accountNum.toString(), style: const TextStyle( fontWeight: FontWeight.w500, fontSize: 16, @@ -234,7 +240,7 @@ class _MyPageState extends ConsumerState { ), ), ), - Text("자유 지정 가능한 계좌 이름", + Text(uvm.accounts.first.accountAlias!, style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600 @@ -293,7 +299,7 @@ class _MyPageState extends ConsumerState { ), ), bottomNavigationBar: const CustomBottomNavigationBar( - index: 0, + index: 4, isIn: true, ), ); diff --git a/lib/view/notification_page.dart b/lib/view/notification_page.dart index ba843ae2..9ef94b8c 100644 --- a/lib/view/notification_page.dart +++ b/lib/view/notification_page.dart @@ -1,10 +1,16 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; import 'package:groupsettlement2/view/shared_basic_widget.dart'; import 'package:groupsettlement2/viewmodel/UserViewModel.dart'; +import 'package:intl/intl.dart'; import '../class/class_alarm.dart'; +import '../class/class_group.dart'; +import '../class/class_settlement.dart'; +import '../class/class_user.dart'; +import '../common_fireservice.dart'; import '../design_element.dart'; class NotificationPage extends ConsumerStatefulWidget { @@ -70,11 +76,11 @@ class _NotificationPageState extends ConsumerState with Ticker indicator: UnderlineTabIndicator( borderSide: BorderSide(width:3,color: cp.color), ), - //indicatorSize: TabBarIndicatorSize.values.first, + indicatorSize: TabBarIndicatorSize.tab, isScrollable: true, tabs:[ Container( - width:size.width*0.3, + width:size.width*0.25, child: Tab( text: "보낼 정산", @@ -87,7 +93,7 @@ class _NotificationPageState extends ConsumerState with Ticker ), ), Container( - width:size.width*0.1, + width:size.width*0.25, child: Tab( text: "기타", ), @@ -230,29 +236,38 @@ class _NotificationPageState extends ConsumerState with Ticker } } -class oneNotification extends StatefulWidget { +class oneNotification extends ConsumerStatefulWidget { final Alarm alarm; final Size size; const oneNotification({Key? key, required this.alarm,required this.size}) : super(key: key); @override - State createState() => _oneNotificationState(); + ConsumerState createState() => _oneNotificationState(); } -class _oneNotificationState extends State { - Color read = Colors.black; +class _oneNotificationState extends ConsumerState { + @override Widget build(BuildContext context) { + + final provider = ref.watch(userProvider); + + Color read = widget.alarm.isRead == true ? Colors.grey : Colors.black; + String timeAgo = provider.getTimeAgo(widget.alarm.time!); + return GestureDetector( - onTap: (){ - setState(() { + onTap: ()async{ + print(widget.alarm.category); + var extra = await provider.linkAlarm(widget.alarm); + print(extra); + context.push(widget.alarm.route!,extra: extra); + + if(widget.alarm.isRead == false) { + widget.alarm.isRead = true; + } + await FireService().updateDoc("alarmlist/${provider.userData.serviceUserId}/myalarmlist", widget.alarm.alarmId!, widget.alarm.toJson()); + - if(read == Colors.grey){ - read = Colors.black; - } else{ - read = Colors.grey; - } - }); }, child: Container( width: widget.size.width*0.9, @@ -260,19 +275,34 @@ class _oneNotificationState extends State { crossAxisAlignment: CrossAxisAlignment.start, children:[ SizedBox(height:10), - Text(widget.alarm.title!, - style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 18, - color: read, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(widget.alarm.title!, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: 18, + color: read, + ), + ), + Text(timeAgo.toString(), + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: 13, + color: read, + ), + ) + ], ), SizedBox(height:5), - Text(widget.alarm.body == null ? "\n" : widget.alarm.body!, - style: TextStyle( - fontWeight: FontWeight.w500, - fontSize: 15, - color: read, + Container( + height:widget.size.height*0.03, + child: Text(widget.alarm.body == null ? "\n" : widget.alarm.body!, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 15, + color: read, + ), ), ), SizedBox(height:10), diff --git a/lib/view/receipt_edit_page.dart b/lib/view/receipt_edit_page.dart index 2cd00532..9b8f262f 100644 --- a/lib/view/receipt_edit_page.dart +++ b/lib/view/receipt_edit_page.dart @@ -345,6 +345,7 @@ class _EditReceiptState extends ConsumerState { return; } } catch (e) { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( '${index + 1}번째 항목의 수량이 잘못되었어요. : ${countController[index].text}'), @@ -357,6 +358,7 @@ class _EditReceiptState extends ConsumerState { .parse(priceController[index].text) .toInt(); } catch (e) { + ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text( '${index + 1}번째 항목의 가격이 잘못되었어요. : ${priceController[index].text}'), diff --git a/lib/viewmodel/UserViewModel.dart b/lib/viewmodel/UserViewModel.dart index d8b24493..2164f12e 100644 --- a/lib/viewmodel/UserViewModel.dart +++ b/lib/viewmodel/UserViewModel.dart @@ -42,6 +42,7 @@ class UserViewModel extends ChangeNotifier { fetchUser(userData.serviceUserId!); fetchSettlement(0, initialStmCount); fetchAlarm(userData.serviceUserId!); + fetchAccount(); return; } @@ -50,6 +51,20 @@ class UserViewModel extends ChangeNotifier { ..sort((e1, e2) => e2.key.time!.compareTo(e1.key.time!))); } + Future> linkAlarm(Alarm alarm)async{ + if(alarm.route == "/MyPage"){ + return []; + } else if(alarm.route == "/GroupSelect/GroupMain"){ + Group group = await Group().getGroupByGroupId(alarm.args[1]); + return [userData,group]; + } else if(alarm.route == "/SettlementInformation"){ + Settlement stm = await Settlement().getSettlementBySettlementId(alarm.args[0]); + Group group = await Group().getGroupByGroupId(alarm.args[1]); + return [stm,group,userData]; + } + return []; + } + Future editUsername(String newName) async{ userData.name = newName; FireService().updateDoc("userlist", userData.serviceUserId!, userData.toJson()); @@ -189,11 +204,6 @@ class UserViewModel extends ChangeNotifier { notifyListeners(); } - //해당 알림 클릭시 이동할 페이지 지정(미구현) - void linkAlarm() { - - } - Future deleteAlarm(int category, Alarm removeAlarm) async { final alarmRef = db.collection("alarmlist/" + userData.serviceUserId! + "/myalarmlist"); @@ -219,12 +229,17 @@ class UserViewModel extends ChangeNotifier { } - void addAccount(Account account) async { + void addAccount(Account account,bool isFavorite) async { final userRef = db.collection("userlist").doc(userData.serviceUserId); db.runTransaction((transaction) async { - userData.accountInfo.add(account.accountId!); - account.creatAccount(userData.serviceUserId!); + if(isFavorite){ + accounts.insert(0,account); + } else { + accounts.add(account); + } + account.createAccount(userData.serviceUserId!); transaction.update(userRef, userData.toJson()); + notifyListeners(); }).then( (value) { print("DocumentSnapshot successfully updated!"); //성공 메시지 @@ -249,9 +264,10 @@ class UserViewModel extends ChangeNotifier { final userRef = db.collection("userlist").doc(userData.serviceUserId); final accountRef = db.collection("accountlist").doc(account.accountId); db.runTransaction((transaction) async { - userData.accountInfo.removeAt(index); + accounts.removeAt(index); transaction.delete(accountRef); transaction.update(userRef, userData.toJson()); + notifyListeners(); }).then( (value) { print("DocumentSnapshot successfully updated!"); //성공 메시지 @@ -265,6 +281,40 @@ class UserViewModel extends ChangeNotifier { notifyListeners(); } + String getGroupRecentActivityTime(Group group){ + Settlement recentStm = Settlement(); + if(group.settlements.isEmpty){ + return ""; + } + for(var stm in settlementInfo.keys){ + if(stm.settlementId == group.settlements.last){ + recentStm = stm; + } + } + return getTimeAgo(recentStm.time!); + } + + String getTimeAgo(Timestamp time){ + DateTime dt = time != null + ? DateTime.parse(time!.toDate().toString()) + : DateTime.utc(1000, 01, 01); + String timeAgo; + + if(DateTime.now().month - dt.month > 0){ + timeAgo = (DateTime.now().month - dt.month).toString() + "개월 전"; + } else if(DateTime.now().day - dt.day > 0){ + timeAgo = (DateTime.now().day - dt.day).toString() + "일 전"; + } else if(DateTime.now().hour - dt.hour > 0){ + timeAgo = (DateTime.now().hour - dt.hour).toString() + "시간 전"; + } else if(DateTime.now().minute - dt.minute > 0){ + timeAgo = (DateTime.now().minute - dt.minute).toString() + "분 전"; + } else { + timeAgo = "방금"; + } + + return timeAgo; + } + double getCurrentMoney(Settlement settlement) { double currentMoney = 0;