Skip to content

Instantly share code, notes, and snippets.

@DevHyeon0312
Created March 15, 2024 01:03
Show Gist options
  • Save DevHyeon0312/714c9890538812dd004fdbcd2fb0583b to your computer and use it in GitHub Desktop.
Save DevHyeon0312/714c9890538812dd004fdbcd2fb0583b to your computer and use it in GitHub Desktop.
Android14 (SDK34) - OneUI 6.0 에서 발생하는 백화현상 임시 대응
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:iamport_flutter_example/screens/certification.dart';
import 'package:iamport_flutter_example/screens/certification_result.dart';
import 'package:iamport_flutter_example/screens/certification_test.dart';
import 'package:iamport_flutter_example/screens/home.dart';
import 'package:iamport_flutter_example/screens/payment.dart';
import 'package:iamport_flutter_example/screens/payment_oneui_issue_test.dart';
import 'package:iamport_flutter_example/screens/payment_result.dart';
import 'package:iamport_flutter_example/screens/payment_test.dart';
void main() {
runApp(IamportApp());
}
class IamportApp extends StatefulWidget {
@override
_IamportAppState createState() => _IamportAppState();
}
class _IamportAppState extends State<IamportApp> {
static const Color primaryColor = Color(0xff344e81);
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
systemNavigationBarColor: Colors.transparent,
statusBarColor: Colors.transparent,
));
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
return GetMaterialApp(
initialRoute: '/',
theme: ThemeData(
primaryColor: primaryColor,
),
getPages: [
GetPage(name: '/', page: () => Home()),
GetPage(name: '/payment-test', page: () => PaymentTest()),
GetPage(name: '/payment', page: () => Payment()),
GetPage(name: '/payment-result', page: () => PaymentResult()),
GetPage(
name: '/payment-oneui-issue-test',
page: () => PaymentOneUiIssueTest(),
),
GetPage(name: '/certification-test', page: () => CertificationTest()),
GetPage(name: '/certification', page: () => Certification()),
GetPage(
name: '/certification-result', page: () => CertificationResult()),
],
);
}
}
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:iamport_flutter/iamport_payment.dart';
import 'package:iamport_flutter/model/payment_data.dart';
class PaymentOneUiIssueTest extends StatefulWidget {
@override
State<PaymentOneUiIssueTest> createState() => _PaymentOneUiIssueTestState();
}
class _PaymentOneUiIssueTestState extends State<PaymentOneUiIssueTest>
with WidgetsBindingObserver {
String userCode = Get.arguments['userCode'] as String;
PaymentData data = Get.arguments['data'] as PaymentData;
bool isLandscape = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
if (await _isSamsungIssueDevice()) {
if (state == AppLifecycleState.resumed) {
_showPortrait();
} else if (state == AppLifecycleState.hidden) {
_shouldLandScape();
}
}
}
Future<bool> _isSamsungIssueDevice() async {
if (Platform.isAndroid) {
/// DeviceInfoPlugin 을 사용하면 sdkIn 34, manufacturer samsung 일 경우에만 적용되도록 처리 가능합니다.
/// (모든 Android 기기에서 이슈에 대한 처리를 하는 것은 비효율적이기 때문입니다.)
// try {
// var androidInfo = await DeviceInfoPlugin().androidInfo;
// var sdkInt = androidInfo.version.sdkInt;
// var manufacturer = androidInfo.manufacturer;
// if (sdkInt == 34 && manufacturer == 'samsung') {
// return true;
// }
// } catch (_) {}
return true;
}
return false;
}
Future<void> _shouldLandScape() async {
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
Future.delayed(const Duration(milliseconds: 500), () {
setState(() {
isLandscape = true;
});
});
}
Future<void> _showPortrait() async {
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
Future.delayed(const Duration(milliseconds: 500), () {
setState(() {
isLandscape = false;
});
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
IamportPayment(
appBar: isLandscape
? null
: AppBar(
title: Text('아임포트 결제'),
centerTitle: true,
titleTextStyle: TextStyle(
fontSize: 24,
color: Colors.white,
),
backgroundColor: Colors.blue,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Get.back();
},
),
),
initialChild: SafeArea(
child: InitialChildWidget(),
),
userCode: userCode,
data: data,
callback: (Map<String, String> result) {
Get.offNamed('/payment-result', arguments: result);
},
),
isLandscape ? InitialChildWidget() : SizedBox(),
],
);
}
}
class InitialChildWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/images/iamport-logo.png'),
Padding(padding: EdgeInsets.symmetric(vertical: 15)),
Text('잠시만 기다려주세요...', style: TextStyle(fontSize: 20.0)),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:iamport_flutter/model/payment_data.dart';
import 'package:iamport_flutter/model/pg/naver/naver_pay_products.dart';
import 'package:iamport_flutter/model/pg/kcp/kcp_products.dart';
import 'package:iamport_flutter_example/model/method.dart';
import 'package:iamport_flutter_example/model/pg.dart';
import 'package:iamport_flutter_example/model/quota.dart';
class PaymentTest extends StatefulWidget {
@override
_PaymentTestState createState() => _PaymentTestState();
}
class _PaymentTestState extends State<PaymentTest> {
final _formKey = GlobalKey<FormState>();
late String userCode; // 가맹점 식별코드
String pg = 'html5_inicis'; // PG사
String payMethod = 'card'; // 결제수단
String cardQuota = '0'; // 할부개월수
late String vbankDue; // 가상계좌 입금기한
late String bizNum; // 사업자번호
bool digital = false; // 실물컨텐츠 여부
bool escrow = false; // 에스크로 여부
late String name; // 주문명
late String amount; // 결제금액
late String merchantUid; // 주문번호
late String buyerName; // 구매자 이름
late String buyerTel; // 구매자 전화번호
late String buyerEmail; // 구매자 이메일
final buttonStyle = ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
elevation: 0,
shadowColor: Colors.transparent,
backgroundColor: Colors.blue,
);
final textStyle = TextStyle(
fontSize: 18,
color: Colors.white,
fontWeight: FontWeight.bold,
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('아임포트 결제 테스트'),
centerTitle: true,
titleTextStyle: TextStyle(
fontSize: 24,
color: Colors.white,
),
backgroundColor: Colors.blue,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Get.back();
},
),
),
body: SafeArea(
minimum: EdgeInsets.symmetric(horizontal: 15),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
decoration: InputDecoration(
labelText: '가맹점 식별코드',
),
validator: (value) =>
value!.isEmpty ? '가맹점 식별코드는 필수입력입니다' : null,
initialValue: '',
onSaved: (String? value) {
userCode = value!;
},
),
DropdownButtonFormField(
decoration: InputDecoration(
labelText: 'PG사',
),
value: pg,
onChanged: (String? value) {
setState(() {
pg = value!;
payMethod = Method.getValueByPg(value);
});
},
items:
Pg.getLists().map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(Pg.getLabel(value)),
);
}).toList(),
),
DropdownButtonFormField(
decoration: InputDecoration(
labelText: '결제수단',
),
value: payMethod,
onChanged: (String? value) {
setState(() {
payMethod = value!;
});
},
items: Method.getListsByPg(pg)
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(Method.getLabel(value)),
);
}).toList(),
),
payMethod == 'card'
? DropdownButtonFormField(
decoration: InputDecoration(
labelText: '할부개월수',
),
value: cardQuota,
onChanged: (String? value) {
setState(() {
cardQuota = value!;
});
},
items: Quota.getListsByPg(pg)
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(Quota.getLabel(value)),
);
}).toList(),
)
: Container(),
payMethod == 'vbank'
? TextFormField(
decoration: InputDecoration(
labelText: '입금기한',
hintText: 'YYYYMMDDhhmm',
),
validator: (value) {
if (value!.isEmpty) return '입금기한은 필수입력입니다';
if (value.length > 0) {
RegExp regex = RegExp(r'^[0-9]+$');
if (!regex.hasMatch(value)) return '입금기한이 올바르지 않습니다.';
}
return null;
},
keyboardType: TextInputType.number,
onSaved: (String? value) {
vbankDue = value!;
},
)
: Container(),
payMethod == 'vbank' && pg == 'danal_tpay'
? TextFormField(
decoration: InputDecoration(
labelText: '사업자번호',
),
validator: (value) {
if (value!.isEmpty) return '사업자번호는 필수입력입니다';
if (value.length > 0) {
RegExp regex = RegExp(r'^[0-9]+$');
if (!regex.hasMatch(value))
return '사업자번호가 올바르지 않습니다.';
if (value.length != 10) return '사업자번호는 10자리 숫자입니다.';
}
return null;
},
keyboardType: TextInputType.number,
onSaved: (String? value) {
bizNum = value!;
},
)
: Container(),
TextFormField(
decoration: InputDecoration(
labelText: '주문명',
),
initialValue: '아임포트 결제 데이터 분석',
validator: (value) => value!.isEmpty ? '주문명은 필수입력입니다' : null,
onSaved: (String? value) {
name = value!;
},
),
TextFormField(
decoration: InputDecoration(
labelText: '결제금액',
),
initialValue: '1000',
validator: (value) {
if (value!.isEmpty) {
return '결제금액은 필수입력입니다.';
}
if (value.length > 0) {
RegExp regex = RegExp(r'^\d+(\.\d+)?$');
if (!regex.hasMatch(value)) return '결제금액이 올바르지 않습니다.';
}
return null;
},
keyboardType: TextInputType.numberWithOptions(decimal: true),
onSaved: (String? value) {
amount = value!;
},
),
TextFormField(
decoration: InputDecoration(
labelText: '주문번호',
),
validator: (value) => value!.isEmpty ? '주문번호는 필수입력입니다' : null,
initialValue: 'mid_${DateTime.now().millisecondsSinceEpoch}',
onSaved: (String? value) {
merchantUid = value!;
},
),
TextFormField(
decoration: InputDecoration(
labelText: '이름',
),
initialValue: '홍길동',
onSaved: (String? value) {
buyerName = value!;
},
),
TextFormField(
decoration: InputDecoration(
labelText: '전화번호',
),
initialValue: '01012341234',
validator: (value) {
if (value!.length > 0) {
RegExp regex = RegExp(r'^[0-9]+$');
if (!regex.hasMatch(value)) return '전화번호가 올바르지 않습니다.';
}
return null;
},
keyboardType: TextInputType.number,
onSaved: (String? value) {
buyerTel = value!;
},
),
TextFormField(
decoration: InputDecoration(
labelText: '이메일',
),
initialValue: 'example@example.com',
keyboardType: TextInputType.emailAddress,
onSaved: (String? value) {
buyerEmail = value!;
},
),
Container(
padding: EdgeInsets.symmetric(vertical: 10),
child: Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
print('creating payment data...');
PaymentData data = PaymentData(
pg: pg,
payMethod: payMethod,
escrow: escrow,
name: name,
amount: num.parse(amount),
merchantUid: merchantUid,
buyerName: buyerName,
buyerTel: buyerTel,
buyerEmail: buyerEmail,
appScheme: 'flutterexample',
niceMobileV2: true,
);
if (payMethod == 'card' && cardQuota != '0') {
data.cardQuota = [];
if (cardQuota != '1') {
data.cardQuota!.add(int.parse(cardQuota));
}
}
// 가상계좌의 경우, 입금기한 추가
if (payMethod == 'vbank') {
data.vbankDue = vbankDue;
// 다날 && 가상계좌의 경우, 사업자 등록번호 10자리 추가
if (pg == 'danal_tpay') {
data.bizNum = bizNum;
}
}
// 휴대폰 소액결제의 경우, 실물 컨텐츠 여부 추가
if (payMethod == 'phone') {
data.digital = digital;
if (pg == 'danal') {
// 다날 && 휴대폰 소액결제의 경우, company 파라메터 추가
data.company = '아임포트';
}
}
// 정기결제의 경우, customer_uid 추가
if (pg == 'kcp_billing') {
data.customerUid =
'cuid_${DateTime.now().millisecondsSinceEpoch}';
}
// 네이버페이 관련 정보 추가
if (pg == 'naverpay') {
NaverPayProducts p = NaverPayProducts(
name: '한국사',
categoryId: 'GENERAL',
categoryType: 'BOOK',
count: 10,
uid: '107922211',
payReferrer: 'NAVER_BOOK',
);
data.naverUseCfm = '20231026';
data.naverCultureBenefit = false;
data.naverPopupMode = false;
data.naverProducts = [p];
}
// kcp 에스크로 관련 정보 추가
if (pg == 'kcp' && escrow == true) {
KcpProducts p = KcpProducts(
orderNumber: 'order1234',
name: '에스크로 주문',
quantity: 3,
amount: 5000,
);
data.kcpProducts = [p];
}
// [이니시스-빌링.나이스.다날] 제공기간 표기
data.period = {
'from': '20230101',
'to': '20231231',
};
data.popup = false;
Get.toNamed(
'/payment',
arguments: {
'userCode': userCode,
'data': data,
},
);
}
},
child: Text(
'결제하기',
style: textStyle,
),
style: buttonStyle,
),
),
SizedBox(
width: 8,
),
Expanded(
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
print('creating payment data...');
PaymentData data = PaymentData(
pg: pg,
payMethod: payMethod,
escrow: escrow,
name: name,
amount: num.parse(amount),
merchantUid: merchantUid,
buyerName: buyerName,
buyerTel: buyerTel,
buyerEmail: buyerEmail,
appScheme: 'flutterexample',
niceMobileV2: true,
);
if (payMethod == 'card' && cardQuota != '0') {
data.cardQuota = [];
if (cardQuota != '1') {
data.cardQuota!.add(int.parse(cardQuota));
}
}
// 가상계좌의 경우, 입금기한 추가
if (payMethod == 'vbank') {
data.vbankDue = vbankDue;
// 다날 && 가상계좌의 경우, 사업자 등록번호 10자리 추가
if (pg == 'danal_tpay') {
data.bizNum = bizNum;
}
}
// 휴대폰 소액결제의 경우, 실물 컨텐츠 여부 추가
if (payMethod == 'phone') {
data.digital = digital;
if (pg == 'danal') {
// 다날 && 휴대폰 소액결제의 경우, company 파라메터 추가
data.company = '아임포트';
}
}
// 정기결제의 경우, customer_uid 추가
if (pg == 'kcp_billing') {
data.customerUid =
'cuid_${DateTime.now().millisecondsSinceEpoch}';
}
// 네이버페이 관련 정보 추가
if (pg == 'naverpay') {
NaverPayProducts p = NaverPayProducts(
name: '한국사',
categoryId: 'GENERAL',
categoryType: 'BOOK',
count: 10,
uid: '107922211',
payReferrer: 'NAVER_BOOK',
);
data.naverUseCfm = '20231026';
data.naverCultureBenefit = false;
data.naverPopupMode = false;
data.naverProducts = [p];
}
// kcp 에스크로 관련 정보 추가
if (pg == 'kcp' && escrow == true) {
KcpProducts p = KcpProducts(
orderNumber: 'order1234',
name: '에스크로 주문',
quantity: 3,
amount: 5000,
);
data.kcpProducts = [p];
}
// [이니시스-빌링.나이스.다날] 제공기간 표기
data.period = {
'from': '20230101',
'to': '20231231',
};
data.popup = false;
Get.toNamed(
'/payment-oneui-issue-test',
arguments: {
'userCode': userCode,
'data': data,
},
);
}
},
child: Text(
'Android34-결제',
style: textStyle,
),
style: buttonStyle,
),
),
],
),
),
],
),
),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment