아이콘을 클릭했을때 다른 페이지로 이동하는 방법
IconButton( onPressed: () { // list add // [HomeScreen(),SettingsScreen()] 마지막에 푸시 push Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { return SettingsScreen(); // SettingsScreen페이지로 이동 }, ), ); },
새로운 스크린 작업 (setting 페이지)
import 'package:flutter/material.dart'; import 'package:lottery_number_randomly/constant/color.dart'; class SettingsScreen extends StatefulWidget { const SettingsScreen({super.key}); @override State<SettingsScreen> createState() => _SettingsScreenState(); } class _SettingsScreenState extends State<SettingsScreen> { double maxNumber = 10000; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: PRIMARY_COLOR, body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Expanded( child: Row( children: maxNumber .toInt() .toString() .split('') .map( (e) => Image.asset( 'asset/img/$e.png', width: 50.0, height: 70.0, ), ) .toList(), ), ), Slider( value: maxNumber, min: 10000, max: 1000000, onChanged: (double val) { setState(() { maxNumber = val; // slide 값이 변경될때마다 build() 를 호출하여 Slider에게 value 값으로써 넣어줘야 바가 움직인다. }); }, ), ElevatedButton( onPressed: () {}, style: ElevatedButton.styleFrom( backgroundColor: RED_COLOR, ), child: Text('저장'), ), ], ), ), ), ); } }
스크린별 데이터 주고 받기
ElevatedButton( onPressed: () { Navigator.of(context).pop(maxNumber.toInt()); // pop : 뒤로가기, pop에 인자로 데이터를 넘겨주면 push를 호출한 페이지에서 받을 수 있음 }, style: ElevatedButton.styleFrom( backgroundColor: RED_COLOR, ), child: Text('저장'), ),
IconButton( onPressed: () async { // 다른 스크린으로 부터 데이터를 돌려받을때는 async (비동기)로 처리해야함 final result = await Navigator.of(context).push<int>( // push를 실행한 곳에서 데이터를 돌려받을 수 있다. MaterialPageRoute( builder: (BuildContext context) { return SettingsScreen(); }, ), ); },
_Header에서 값을 받아왔으므로 State쪽으로 받아온 데이터를 올려서 한번에 관리되어야한다. 그럼 _Header 클래스에 VoidCallback onPressed 정의하고 State 클래스에서 _Header 클래스 호출시 매개변수로 넘기도록 한다.
코드 정리 (State에서 상태 관리)
import 'dart:math'; import 'package:flutter/material.dart'; import 'package:lottery_number_randomly/constant/color.dart'; import 'package:lottery_number_randomly/screen/settings_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { int maxNumber = 1000; List<int> randomNumbers = [ 123, 456, 789, ]; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: PRIMARY_COLOR, body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _Header(onPressed: onSettingsPop), _Body(randomNumbers: randomNumbers), _Footer(onPressed: onRandomNumberGenerate), ], ), ), ), ); } void onSettingsPop() async { // 다른 스크린으로 부터 데이터를 돌려받을때는 async (비동기)로 처리해야함 /* * 아무것도 안돌려받을 수도 있음. 버튼을 누르지 않고 그냥 뒤로가기를 눌렀을때 * 데이터를 리턴 받는 페이지는 항상 들어오는 데이터가 null 될 수 있다는 가정하에 코딩을 해야함 * */ final int? result = await Navigator.of(context).push<int>( MaterialPageRoute( builder: (BuildContext context) { return SettingsScreen(); }, ), ); if(result != null){ setState(() { maxNumber = result; }); } } void onRandomNumberGenerate() { final rand = Random(); final Set<int> newNumbers = {}; // 중복 숫자 제거하기 위해 Set while (newNumbers.length != 3) { final number = rand.nextInt(maxNumber); // max 1000 이하 숫자 랜덤생성 newNumbers.add(number); } setState(() { randomNumbers = newNumbers.toList(); }); } } class _Header extends StatelessWidget { final VoidCallback onPressed; const _Header({required this.onPressed, super.key}); @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '랜덤 숫자 생성기', style: TextStyle( color: Colors.white, fontSize: 30.0, fontWeight: FontWeight.w700, ), ), IconButton( onPressed: onPressed, icon: Icon( Icons.settings, color: RED_COLOR, ), ), ], ); } } class _Body extends StatelessWidget { final List<int> randomNumbers; const _Body({required this.randomNumbers, super.key}); @override Widget build(BuildContext context) { return Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: randomNumbers .asMap() .entries .map( (x) => Padding( padding: EdgeInsets.only( bottom: x.key == 2 ? 0 : 16.0), // 마지막 7,8,9 아래는 padding을 넣지 않음 child: Row( children: x.value .toString() .split('') .map( (y) => Image.asset( 'asset/img/$y.png', height: 70.0, width: 50.0, ), ) .toList(), ), ), ) .toList(), ), ); } } class _Footer extends StatelessWidget { final VoidCallback onPressed; // onPress 함수를 외부에서 받아오는 방법 외워둘것 const _Footer({required this.onPressed, super.key}); @override Widget build(BuildContext context) { return SizedBox( width: double.infinity, // width 무한대 child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: RED_COLOR, ), onPressed: onPressed, child: Text('생성하기'), ), ); } }
setting screen 데이터 유지(home으로 갔다가 다시 setting_screen으로 넘어왔을때)
pop()을 통해 데이터를 넘겨주고 나면, 기존 setting screen 의 state는 모두 dispose() 되고 다시 setting screen 으로 넘어갈때는 새로운 state 를 생성해서 넘어간다. 그래서 항상 1000으로 고정되어있는 것을 볼 수 있다.
이 값을 유지하기 위해서는 받은 maxNumbe 값을 settings screen state 가 생성될때 다시 넘겨주면 됨.
//settings_screen.dart final int? result = await Navigator.of(context).push<int>( MaterialPageRoute( builder: (BuildContext context) { return SettingsScreen( maxNumber: maxNumber, ); }, ), );
//settings_screen.dart final int maxNumber; const SettingsScreen({required this.maxNumber, super.key}); void initState() { // // TODO: implement initState super.initState(); maxNumber = widget.maxNumber .toDouble(); // build() 가 되기 전, home 스크린으로 부터 받은 maxNumber를 변경해준다. }
최종
// lib/screen/home_screen.dart import 'dart:math'; import 'package:flutter/material.dart'; import 'package:lottery_number_randomly/component/number_row.dart'; import 'package:lottery_number_randomly/constant/color.dart'; import 'package:lottery_number_randomly/screen/settings_screen.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { int maxNumber = 1000; List<int> randomNumbers = [ 123, 456, 789, ]; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: PRIMARY_COLOR, body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _Header(onPressed: onSettingsPop), _Body(randomNumbers: randomNumbers), _Footer(onPressed: onRandomNumberGenerate), ], ), ), ), ); } void onSettingsPop() async { // 다른 스크린으로 부터 데이터를 돌려받을때는 async (비동기)로 처리해야함 /* * 아무것도 안돌려받을 수도 있음. 버튼을 누르지 않고 그냥 뒤로가기를 눌렀을때 * 데이터를 리턴 받는 페이지는 항상 들어오는 데이터가 null 될 수 있다는 가정하에 코딩을 해야함 * */ final int? result = await Navigator.of(context).push<int>( MaterialPageRoute( builder: (BuildContext context) { return SettingsScreen( maxNumber: maxNumber, ); }, ), ); if (result != null) { setState(() { maxNumber = result; }); } } void onRandomNumberGenerate() { final rand = Random(); final Set<int> newNumbers = {}; // 중복 숫자 제거하기 위해 Set while (newNumbers.length != 3) { final number = rand.nextInt(maxNumber); // max 1000 이하 숫자 랜덤생성 newNumbers.add(number); } setState(() { randomNumbers = newNumbers.toList(); }); } } class _Header extends StatelessWidget { final VoidCallback onPressed; const _Header({required this.onPressed, super.key}); @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '랜덤 숫자 생성기', style: TextStyle( color: Colors.white, fontSize: 30.0, fontWeight: FontWeight.w700, ), ), IconButton( onPressed: onPressed, icon: Icon( Icons.settings, color: RED_COLOR, ), ), ], ); } } class _Body extends StatelessWidget { final List<int> randomNumbers; const _Body({required this.randomNumbers, super.key}); @override Widget build(BuildContext context) { return Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: randomNumbers .asMap() .entries .map( (x) => Padding( padding: EdgeInsets.only( bottom: x.key == 2 ? 0 : 16.0), // 마지막 7,8,9 아래는 padding을 넣지 않음 child: NumberRow( number: x.value, ), ), ) .toList(), ), ); } } class _Footer extends StatelessWidget { final VoidCallback onPressed; // onPress 함수를 외부에서 받아오는 방법 외워둘것 const _Footer({required this.onPressed, super.key}); @override Widget build(BuildContext context) { return SizedBox( width: double.infinity, // width 무한대 child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: RED_COLOR, ), onPressed: onPressed, child: Text('생성하기'), ), ); } }
// lib/screen/settings_screen.dart import 'package:flutter/material.dart'; import 'package:lottery_number_randomly/component/number_row.dart'; import 'package:lottery_number_randomly/constant/color.dart'; class SettingsScreen extends StatefulWidget { final int maxNumber; const SettingsScreen({required this.maxNumber, super.key}); @override State<SettingsScreen> createState() => _SettingsScreenState(); } class _SettingsScreenState extends State<SettingsScreen> { double maxNumber = 1000; @override void initState() { // // TODO: implement initState super.initState(); maxNumber = widget.maxNumber .toDouble(); // build() 가 되기 전, home 스크린으로 부터 받은 maxNumber를 변경해준다. } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: PRIMARY_COLOR, body: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _Body(maxNumber: maxNumber), _Footer( maxNumber: maxNumber, onSliderChanged: onSliderChanged, onButtonPressed: onButtonPressed), ], ), ), ), ); } void onSliderChanged(double val) { setState(() { maxNumber = val; // slide 값이 변경될때마다 build() 를 호출하여 Slider에게 value 값으로써 넣어줘야 바가 움직인다. }); } void onButtonPressed() { Navigator.of(context).pop(maxNumber.toInt()); } } class _Body extends StatelessWidget { final double maxNumber; const _Body({required this.maxNumber, Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Expanded( child: NumberRow( number: maxNumber.toInt(), ), ); } } class _Footer extends StatelessWidget { final ValueChanged<double>? onSliderChanged; final double maxNumber; final VoidCallback onButtonPressed; const _Footer({ required this.maxNumber, required this.onSliderChanged, required this.onButtonPressed, Key? key, }) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Slider( value: maxNumber, min: 1000, max: 100000, onChanged: onSliderChanged, ), ElevatedButton( onPressed: onButtonPressed, style: ElevatedButton.styleFrom( backgroundColor: RED_COLOR, ), child: Text('저장'), ), ], ); } }
// lib/component/number_row.dart import 'package:flutter/material.dart'; class NumberRow extends StatelessWidget { final int number; const NumberRow({required this.number, Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Row( children: number .toString() .split('') .map( (e) => Image.asset( 'asset/img/$e.png', width: 50.0, height: 70.0, ), ) .toList(), ); } }