[flutter] 랜덤숫자 생성기 : Navigation, Button, Slider, 난수생성(Random Number)

jini.choi·2024년 4월 10일
0

flutter

목록 보기
7/9

레이아웃 생성

ElevatedButton

Style입히기

  • 여러가지 버튼들이 모두 스타일 안에다가 실제 사용한 버튼을 넣어주고 StyleFrom()을 사용
    style: ElevatedButton.styleFrom()
ElevatedButton(
                  style: ElevatedButton.styleFrom(
                    backgroundColor: RED_COLOR, //주색상
                  ),
                  onPressed: () {},
                  child: const Text(
                    "생성하기!",
                    style: TextStyle(
                      color: Colors.white,
                    ),
                  ),
                ),

map을 사용해서 여러 이미지 불러오기

  • 세로 3줄안에 가로로 3개씩 이미지로 뿌려주기 위해 map을 2중으로 사용한다.
Expanded(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [123, 456, 789]
        .map((x) => Row(
              children: x
                  .toString()
                  .split('')
                  .map((y) => Image.asset(
                        'assets/images/ranNum/$y.png',
                        width: 50,
                        height: 70,
                      ))
                  .toList(), // .toList()를 추가하여 Iterable을 List로 변환
            ))
        .toList(), // 여기에도 .toList() 추가
  ),
),

조건에 따라 패딩넣기(마지막빼고 패딩 넣기)

  • [123, 456, 789] 을 asMap으로 List에서 Map 으로 변경한다 그러면 index값이 키가 되고 인덱스에 있는 값들이 Map의 key들이 된다.

  • 그러고 나서 entries.toList().value, key를 체이닝하여 Map의 값이나 키만 가져올 수 있다.

    entries : 키-값 쌍의 배열을 반환

  • 이제 key를 가져와서 index가 마지막숫자면 패딩값을 0으로 주고 false면 16으로 주는 조건을 추가한다.

  • Row의 들어갈 x값은 value를 가져와야하기 때문에 x -> x.value로 수정

Expanded(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [123, 456, 789]
        .asMap()
        .entries
        .map((x) => Padding(
              padding:
                  EdgeInsets.only(bottom: x.key == 2 ? 0 : 16),
              child: Row(
                children: x.value
                    .toString()
                    .split('')
                    .map((y) => Image.asset(
                          'assets/images/ranNum/$y.png',
                          width: 50,
                          height: 70,
                        ))
                    .toList(), // .toList()를 추가하여 Iterable을 List로 변환
              ),
            ))
        .toList(), // 여기에도 .toList() 추가
  ),
),

난수생성(Random Number)

  • 숫자가 위에까지 고정으로 있었다면 랜덤으로 돌리기 위해 state로 관리해야함
class _RanNumScreenState extends State<RanNumScreen> {
   List<int> randumNumbers = [123, 456, 789];
   
  @override
.
.
Expanded(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: randumNumbers
                      .asMap()
                      .entries
                      .map((x) => Padding(
                            padding:
                                E
  • ElevatedButton버튼을 눌렀을 떄 랜던 숫자들을 리스트로 생성해서 randumNumbers state의 randumNumbers list를 변경하도록 한다.

    • 랜덤넘버를 랜덤한 숫자를 생성할 수 있는 클래스를 가져옴(dart:math 임포트해줘야함)
    • nextInt(max)를 통해 최대값을 정해서 그 안에서 랜덤숫자를 생성시킬 수 있다.rand.nextInt(1000)
    • 이제 3개의 랜덤숫자가 들어가 있는 리스트를 새로 만들어서 randumNumbers변수를 대체해준다.
 onPressed: () {
  final rand = Random();

  final List<int> newNumbers = [];

  for (int i = 0; i < 3; i++) {
    final number = rand.nextInt(1000);

    newNumbers.add(number);
  }

  setState(() {
    randumNumbers = newNumbers;
  });
},  
  
  
//중복을 없애고 싶을 땐 List가 아닌 Set을 사용(randumNumbers는 List이기 때문에 newNumbers.toList()를 해줘야함)
// for을 쓰면 중복이 있을 때 아예 안나오기 때문에 while문으로 변경
onPressed: () {
  final rand = Random();

  final Set<int> newNumbers = {};

  while (newNumbers.length != 3) {  //newNumbers.length 길이가 3이 아닐때까지 반복
    final number = rand.nextInt(1000);

    newNumbers.add(number);
  }

  setState(() {
    randumNumbers = newNumbers.toList();
  });
},

Navigation

  • Navigator 클래서 가져와서 .of(context)로 WidgetTree에 있는 가장 가까운 Navigator를 가져와준다. 그리고 push를 통해 새로운 화면을 띄우는 라우터로 연결해준다.
IconButton(
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (BuildContext context) {
                  return const SettingsScreen();
                },
              ),
            );
          },
          icon: const Icon(Icons.settings),
          color: RED_COLOR,
        ),

Slider위젯

  • maxNum이라고 변수를 할당시켜줌
class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  double maxNum = 1000;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
    .
    .
    
  • onChangede될때 마자 빌드가 되야하기 때문에setState안에 maxNum를 할당해준다.
Slider(
                  value: maxNum,
                  min: 1000,
                  max: 100000,
                  onChanged: (double val) {
                    setState(() {
                      maxNum = val;
                    });
                  },
                ),
  • slide에 따라 바뀌는 숫자값을 화면에 그려주기(maxNum는 더블이라서 int로 변경해줘야함)
 Expanded(
                  child: Row(
                    children: maxNum
   						.toInt()
                        .toString()
                        .split('')
                        .map((e) => Image.asset(
                              'assets/images/ranNum/$e.png',
                              width: 50,
                              height: 70,
                            ))
                        .toList(),
                  ),
                ),

SettingsScreen 전체코드

import 'dart:ffi';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ver1/constant/color.dart';

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  double maxNum = 1000;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: PRIMARY_COLOR,
        body: SafeArea(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                _Body(maxNum: maxNum),
                _Footer(
                    maxNum: maxNum,
                    onSliderChanged: onSliderChanged,
                    onButtonPressed: onButtonPressed),
              ],
            ),
          ),
        ));
  }

  void onSliderChanged(double val) {
    setState(() {
      maxNum = val;
    });
  }

  void onButtonPressed() {
    Navigator.of(context).pop(maxNum.toInt());
  }
}

class _Footer extends StatelessWidget {
  final double maxNum;
  final ValueChanged<double>? onSliderChanged;
  final VoidCallback onButtonPressed;

  const _Footer({
    super.key,
    required this.maxNum,
    required this.onSliderChanged,
    required this.onButtonPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Slider(
          value: maxNum,
          min: 1000,
          max: 100000,
          onChanged: onSliderChanged,
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(backgroundColor: RED_COLOR),
          onPressed: onButtonPressed,
          child: const Text(
            "저장!",
            style: TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ],
    );
  }
}

class _Body extends StatelessWidget {
  const _Body({
    super.key,
    required this.maxNum,
  });

  final double maxNum;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Row(
        children: maxNum
            .toInt()
            .toString()
            .split('')
            .map((e) => Image.asset(
                  'assets/images/ranNum/$e.png',
                  width: 50,
                  height: 70,
                ))
            .toList(),
      ),
    );
  }
}

뒤로가기 하면서 SettingsScreen에서 저장한 숫자를 HomeScreen Max값으로 전달해주기

뒤로가기

  • pop하면 현재화면을 터뜨리면서 인자에 maxNum를 int로 변형해서 보내준다.
ElevatedButton(
  style: ElevatedButton.styleFrom(backgroundColor: RED_COLOR),
  onPressed: () {
    Navigator.of(context).pop(maxNum.toInt());
  },
  child: const Text(
    "저장!",
    style: TextStyle(
      color: Colors.white,
    ),
  ),
)

HomeScreen에서 값 받아오기

  • 미래에 값을 받아오는거기 때문에 async로 받는다.

  • 유저가 저장을 누르지않고 뒤로가기를 할 수 있기 때문에 null이 아닐때만 maxNumber에 result값을 넣어줘야 한다.

void onSettingsPressed() async {
    final int? result = await Navigator.of(context).push<int>(
      MaterialPageRoute(
        builder: (BuildContext context) {
          return const SettingsScreen();
        },
      ),
    );

    if (result != null) {
      setState(() {
        maxNumber = result;
      });
    }
  }

HomeScreen에서 SettingsScreen으로 값(변수) 전달하기

  • HomeScreen에서 SettingsScreen에 maxNumber를 인자로 전달
void onSettingsPressed() async {
    final int? result = await Navigator.of(context).push<int>(
      MaterialPageRoute(
        builder: (BuildContext context) {
          return SettingsScreen(
            maxNumber: maxNumber,
          );
        },
      ),
    );

    if (result != null) {
      setState(() {
        maxNumber = result;
      });
    }
  }
  • SettingsScreen은 final int maxNumber;로 받아서 컨스트럭트에도 만들어준다.

  • 이제 기존 1000이라고 설정되어 있던 숫자를 home에서 받은 maxNumber로 바꿔준다.

기존

class _SettingsScreenState extends State<SettingsScreen> {
  double maxNum = 1000;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: PRIMARY_COLOR,
        body: SafeArea(
          child: Padding(

수정

  • initState는 SettingsScreenState가 생성되는 순간 StatefulWidget이 값이 변경될 때는 다시 실행이 안되고 SettingsScreenState가 재생성되는 순간에만 실행된다.
class _SettingsScreenState extends State<SettingsScreen> {
  double maxNum = 1000;

  @override
  void initState() {
    super.initState();

    maxNum = widget.maxNumber.toDouble();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: PRIMARY_COLOR,
        body: SafeArea(
          child: Paddi

RanNumScreen 전체 코드

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_ver1/constant/color.dart';
import 'package:flutter_ver1/pages/jin_ranNum/settings_screen.dart';

class RanNumScreen extends StatefulWidget {
  const RanNumScreen({super.key});

  @override
  State<RanNumScreen> createState() => _RanNumScreenState();
}

class _RanNumScreenState extends State<RanNumScreen> {
  int maxNumber = 1000;
  List<int> randumNumbers = [123, 456, 789];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: PRIMARY_COLOR,
      body: SafeArea(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _Header(onPressed: onSettingsPressed),
              _Body(randumNumbers: randumNumbers),
              _Footer(onPressed: onRandomNumberGenerate)
            ],
          ),
        ),
      ),
    );
  }

  void onRandomNumberGenerate() {
    final rand = Random();

    final Set<int> newNumbers = {};

    while (newNumbers.length != 3) {
      final number = rand.nextInt(maxNumber);

      newNumbers.add(number);
    }

    setState(() {
      randumNumbers = newNumbers.toList();
    });
  }

  void onSettingsPressed() async {
    final int? result = await Navigator.of(context).push<int>(
      MaterialPageRoute(
        builder: (BuildContext context) {
          return SettingsScreen(
            maxNumber: maxNumber,
          );
        },
      ),
    );

    if (result != null) {
      setState(() {
        maxNumber = result;
      });
    }
  }
}

class _Body extends StatelessWidget {
  const _Body({
    super.key,
    required this.randumNumbers,
  });

  final List<int> randumNumbers;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: randumNumbers
            .asMap()
            .entries
            .map((x) => Padding(
                  padding: EdgeInsets.only(bottom: x.key == 2 ? 0 : 16),
                  child: Row(
                    children: x.value
                        .toString()
                        .split('')
                        .map((y) => Image.asset(
                              'assets/images/ranNum/$y.png',
                              width: 50,
                              height: 70,
                            ))
                        .toList(), // .toList()를 추가하여 Iterable을 List로 변환
                  ),
                ))
            .toList(), // 여기에도 .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: [
        const Text(
          "랜덤 숫자 생성기",
          style: TextStyle(
            color: Colors.white,
            fontSize: 30,
            fontWeight: FontWeight.w700,
          ),
        ),
        IconButton(
          onPressed: onPressed,
          icon: const Icon(Icons.settings),
          color: RED_COLOR,
        ),
      ],
    );
  }
}

class _Footer extends StatelessWidget {
  final VoidCallback onPressed;
  const _Footer({required this.onPressed, super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: double.infinity,
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          backgroundColor: RED_COLOR, //주색상
        ),
        onPressed: onPressed,
        child: const Text(
          "생성하기!",
          style: TextStyle(
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}

SettingsScreen 전체 코드

import 'dart:ffi';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ver1/constant/color.dart';

class SettingsScreen extends StatefulWidget {
  final int maxNumber;

  const SettingsScreen({super.key, required this.maxNumber});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  double maxNum = 1000;

  @override
  void initState() {
    super.initState();

    maxNum = widget.maxNumber.toDouble();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: PRIMARY_COLOR,
        body: SafeArea(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                _Body(maxNum: maxNum),
                _Footer(
                    maxNum: maxNum,
                    onSliderChanged: onSliderChanged,
                    onButtonPressed: onButtonPressed),
              ],
            ),
          ),
        ));
  }

  void onSliderChanged(double val) {
    setState(() {
      maxNum = val;
    });
  }

  void onButtonPressed() {
    Navigator.of(context).pop(maxNum.toInt());
  }
}

class _Footer extends StatelessWidget {
  final double maxNum;
  final ValueChanged<double>? onSliderChanged;
  final VoidCallback onButtonPressed;

  const _Footer({
    super.key,
    required this.maxNum,
    required this.onSliderChanged,
    required this.onButtonPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        Slider(
          value: maxNum,
          min: 1000,
          max: 100000,
          onChanged: onSliderChanged,
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(backgroundColor: RED_COLOR),
          onPressed: onButtonPressed,
          child: const Text(
            "저장!",
            style: TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ],
    );
  }
}

class _Body extends StatelessWidget {
  const _Body({
    super.key,
    required this.maxNum,
  });

  final double maxNum;

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Row(
        children: maxNum
            .toInt()
            .toString()
            .split('')
            .map((e) => Image.asset(
                  'assets/images/ranNum/$e.png',
                  width: 50,
                  height: 70,
                ))
            .toList(),
      ),
    );
  }
}
profile
개발짜🏃‍♀️

0개의 댓글