[앱개발]/Flutter, Dart, Figma

[플러터] 주사위 앱 만들기

기록자_Recordian 2025. 5. 24. 13:16
728x90
반응형
이전 내용
 

[플러터] D-Day 앱 만들기 (feat. 폰트 반영하기, Cupertino 위젯)

이전 내용 [플러터] 이미지 롤링 기능 구현하기이전 내용 [플러터] 웹 앱 만들어보기에러 관련 [플러터] 에러 발생 및 해결1. Gradle build failed to produce an .apk file...import 'package:flutter/material.dart';void ma

puppy-foot-it.tistory.com

 

◆ 에러 관련

 

[플러터] 에러 발생 및 해결

1. Gradle build failed to produce an .apk file...import 'package:flutter/material.dart';void main() { runApp( MaterialApp( // 머터리얼 디자인 위젯 home: Scaffold( // Scaffold 위젯 body: Center( // 가운데 정렬 child: Text( // Text 위젯 'He

puppy-foot-it.tistory.com

 

◆ 깃허브 업로드 관련

 

[플러터] 대용량 파일 깃허브 업로드하기 (안드로이드 스튜디오)

대용량 파일 깃허브 업로드(안드로이드 스튜디오) 플러터 공부를 하다가 만들어둔 작업물들을 깃허브에 올리려고 하는데, 파일 용량이 크고, 파일 갯수도 많아서 GUI로 업로드가 안 된다. 이전에

puppy-foot-it.tistory.com


주사위 앱 만들기

 

핸드폰을 흔들면 새로운 주사위를 뽑는 앱 만들기

  • 두 개의 탭 만들기
  • 첫 번째 탭: 주사위 숫자 보여주기 (난수 생성)
  • 두 번째 탭: 흔들기의 민감도 정하기 (슬라이더)

파일 구조

 

- lib

/asset/img

/const

/screen

main.dart

 

- pubspec.yaml

 


코드 및 설명

 

◆ const/colors.dart

import 'package:flutter/material.dart';

const backgroundColor = Color(0xFF0E0E0E); // 배경색
const primaryColor = Colors.white; // 메인 색상
final secondaryColor = Colors.grey[600]; // 보조 색상

 

 

◆ screen/ home_screen.dart

import 'package:flutter/material.dart';
import 'package:random_dice/const/colors.dart';

class HomeScreen extends StatelessWidget {
  final int number;

  const HomeScreen({
    required this.number,
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        // 주사위 이미지
        Center(
          child: Image.asset('asset/img/$number.png'),
        ),
        SizedBox(height: 32.0),
        Text(
          '행운의 숫자',
          style: TextStyle(
            color: secondaryColor,
            fontSize: 20.0,
            fontWeight: FontWeight.w700,
          ),
        ),
        SizedBox(height: 12.0),
        Text(
          number.toString(), // 주사위 값 숫자
          style: TextStyle(
            color: primaryColor,
            fontSize: 60.0,
            fontWeight: FontWeight.w200,
          ),
        ),
      ],
    );
  }
}

 

 

◆ screen/ root_screen.dart

import 'package:flutter/material.dart';
import 'dart:math';
import 'package:shake/shake.dart';

class RootScreen extends StatefulWidget {
  const RootScreen({Key? key}) : super(key: key);

  @override
  State<RootScreen> createState() => _RootScreenState();
}

// TickerProviderStateMixin 사용
class _RootScreenState extends State<RootScreen> with TickerProviderStateMixin {
  TabController? controller; // 사용할 TabController 선언
  double threshold = 2.5; // 민감도 기본값 설정
  int number = 1; // 주사위 숫자
  ShakeDetector? shakeDetector;

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

    controller = TabController(length: 2, vsync: this); // 컨트롤러 초기화

    // 컨트롤러 속성 변경 시마다 실행될 함수 등록
    controller!.addListener(tabListener);

    shakeDetector = ShakeDectector.autoStart( // 흔들기 감지 즉시 시작
      shakeSlopTimeMS: 100, // 감지 주기
      shakeThresholdGravity: threshold, // 감지 민감도
      onPhoneShake: onPhoneShake, // 감지 후 실행 함수
    );
  }

  void onPhoneShake() { // 감지 후 실행 함수
    final rand = new Random();

    setState(() {
      number = rand.nextInt(5) + 1;
  });
}

  tabListener() { // 리스너로 사용할 함수
    setState(() {});
  }

  @override
  dispose() {
    controller!.removeListener(tabListener); // 리스너에 등록한 함수 등록 취소
    shakeDetector!.stopListening(); // 흔들기 감지 중지
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TabBarView( // 탭 화면 보여줄 위젯
        controller: controller,
        children: renderChildren(),
      ),
      // 아래 탭 내비게이션 구현 매개변수
      bottomNavigationBar: renderBottomNavigation(),
    );
  }

  List<Widget> renderChildren() {
    return [
      HomeScreen(number: number), // 핸드폰 흔들 때마다 생성될 숫자
      SettingsScreen(
        threshold: threshold,
        onThresholdChange: onThresholdChange,
      ),
    ];
  }
  // 슬라이더값 변경 시 실행 함수
  void onThresholdChange(double val) {
    setState(() {
      threshold = val;
    });
  }

  BottomNavigationBar renderBottomNavigation() {
    // 탭 내비게이션 구현 위젯
    return BottomNavigationBar(
      // 현재 화면에 렌더링되는 탭 인덱스
      currentIndex: controller!.index,
      onTap: (int index) { // 탭 선택시마다 실행될 함수
        setState(() {
          controller!.animateTo(index);
        });
      },
      items: [
        BottomNavigationBarItem( // 하단 탭 바의 각 버튼 구현
          icon: Icon(
            Icons.edgesensor_high_outlined,
          ),
          label: '주사위',
        ),
        BottomNavigationBarItem(
          icon: Icon(
            Icons.settings,
          ),
          label: '설정',
        ),
      ],
    );
  }
}

 

 

◆ screen/ settings_screen.dart

import 'package:random_dice/const/colors.dart';
import 'package:flutter/material.dart';

class SettingsScreen extends StatelessWidget {
  final double threshold; // Slider 현재 값
  // Slider 변경 시마다 실행되는 함수
  final ValueChanged<double> onThresholdChange;

  const SettingsScreen({
    Key? key,

    // threshold와 onThresholdChange는 SettingsScreen에서 입력
    required this.threshold,
    required this.onThresholdChange,
}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Padding(
          padding: const EdgeInsets.only(left: 20.0),
          child: Row(
            children: [
              Text(
                '민감도',
                style: TextStyle(
                  color: secondaryColor,
                  fontSize: 20.0,
                  fontWeight: FontWeight.w700,
                ),
              ),
            ],
          ),
        ),
        Slider(
          min: 0.1, // 최솟값
          max: 10.0, // 최댓값
          divisions: 101, // 최솟값과 최댓값 사이 구간 개수
          value: threshold, // 슬라이더 선택 값
          onChanged: onThresholdChange, // 값 변경 시 실행되는 함수
          label: threshold.toStringAsFixed(1), // 표시되는 값
        ),
      ],
    );
  }
}

 

◆ main.dart

import 'package:flutter/material.dart';
import 'package:random_dice/screen/root_screen.dart';
import 'package:random_dice/const/colors.dart';

void main() {
  runApp(
    MaterialApp(
      theme: ThemeData(
        scaffoldBackgroundColor: backgroundColor,
        sliderTheme: SliderThemeData( // Slider 위젯 관련 테마
          thumbColor: primaryColor, // 노브 색상
          activeTrackColor: primaryColor, // 노브가 이동한 트랙 색상

          // 노브가 아직 이동하지 않은 트랙 색상
          inactiveTrackColor: primaryColor.withOpacity(0.3,),
        ),

        // BottomNavigationBar 위젯 관련 테마
        bottomNavigationBarTheme: BottomNavigationBarThemeData(
          selectedItemColor: primaryColor, // 선택 상태 색
          unselectedItemColor: secondaryColor, // 비선택 상태 색
          backgroundColor: backgroundColor, // 배경색
        ),
      ),
      home: RootScreen(),
    ),
  );
}

 

 

◆ pubspec.yaml

name: random_dice
description: "A new Flutter project."

publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: ^3.8.0

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8
  shake: ^2.2.0 # 흔들림 감지 플러그인

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^5.0.0

# 의존성 덮어쓰기
dependency_overrides:
  sensors_plus: 6.1.0 # sensors_plus 6.1.0 버전 덮어쓰기

flutter:
  uses-material-design: true

    assets:
      - asset/img/

 

 


[참고]

코드팩토리의 플러터 프로그래밍

 


다음 내용

 

 

728x90
반응형