flutter-2024.pdf

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
        useMaterial3: true,
      ),
      home: LayoutDemoPage(),
    );
  }
}

class LayoutDemoPage extends StatelessWidget {
  const LayoutDemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.primaryContainer,
        title: Text(
          'Flutter Layout Demo',
          style: TextStyle(
              color: Theme.of(context).colorScheme.onPrimaryContainer),
        ),
      ),
      body: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(
            child: Column(
              children: [
                Padding(
                  padding: const EdgeInsets.only(bottom: 8),
                  child: Text(
                    '스위스에 멋지 캠프 그라운드',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
                Text(
                  '캔더스태그 / 스위스',
                  style: TextStyle(color: Colors.grey.shade500),
                ),
              ],
            ),
          ),
          Icon(
            Icons.star,
            color: Colors.red,
          ),
          Text('41')
        ],
      ),
    );
  }
}

Flutter Layout Demo 코드 (리팩토링 후)

  1. main.dart
  2. views/layout_demo_page.dart
  3. components/icon_group.dart
// main.dart
import 'package:flutter/material.dart';

import 'views/layout_demo_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
        useMaterial3: true,
      ),
      home: LayoutDemoPage(),
    );
  }
}

//views/layout_demo_page.dart
import 'package:flutter/material.dart';
import '../components/icon_group.dart';

class LayoutDemoPage extends StatelessWidget {
  const LayoutDemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.primaryContainer,
        title: Text(
          'Flutter Layout Demo',
          style: TextStyle(
              color: Theme.of(context).colorScheme.onPrimaryContainer),
        ),
      ),
      body: Column(
        children: [
          Image.asset(
            'assets/m.webp',
            width: 600,
            height: 190,
            fit: BoxFit.cover,
          ),
          Padding(
            padding: const EdgeInsets.all(32),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Expanded(
                  child: Column(
                    children: [
                      Padding(
                        padding: const EdgeInsets.only(bottom: 8),
                        child: Text(
                          '스위스에 멋지 캠프 그라운드',
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ),
                      Text(
                        '캔더스태그 / 스위스',
                        style: TextStyle(color: Colors.grey.shade500),
                      ),
                    ],
                  ),
                ),
                Icon(
                  Icons.star,
                  color: Colors.red,
                ),
                Text('41')
              ],
            ),
          ),
          IconGroup(),
          Padding(
            padding: const EdgeInsets.all(32),
            child: Text(
              'Lake Oeschinen lies at the foot of the Blüemlisalp in the '
              'Bernese Alps. Situated 1,578 meters above sea level, it '
              'is one of the larger Alpine Lakes. A gondola ride from '
              'Kandersteg, followed by a half-hour walk through pastures '
              'and pine forest, leads you to the lake, which warms to 20 '
              'degrees Celsius in the summer. Activities enjoyed here '
              'include rowing, and riding the summer toboggan run.',
              softWrap: true,
            ),
          )
        ],
      ),
    );
  }
}

// components/icon_group.dart
import 'package:flutter/material.dart';

class IconGroup extends StatelessWidget {
  const IconGroup({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Column(
          children: [Icon(Icons.call), Text('CALL')],
        ),
        Column(
          children: [Icon(Icons.route), Text('ROUTE')],
        ),
        Column(
          children: [Icon(Icons.share), Text('SHARE')],
        ),
      ],
    );
  }
}


2일차

상태관리

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: GestureDemoPage(),
    );
  }
}

class GestureDemoPage extends StatefulWidget {
  GestureDemoPage({super.key});

  @override
  State<GestureDemoPage> createState() => _GestureDemoPageState();
}

class _GestureDemoPageState extends State<GestureDemoPage> {
  int myNumber = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$myNumber'),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  myNumber++;
                });
                print(myNumber);
              },
              child: Text('눌러주세요'),
            ),
          ],
        ),
      ),
    );
  }
}

주사위 굴리기 최종 코드

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.red),
        useMaterial3: true,
      ),
      home: const DicePage(),
    );
  }
}

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

  @override
  State<DicePage> createState() => _DicePageState();
}

class _DicePageState extends State<DicePage> {
  int _firstDiceNumber = 3;
  int _secondDiceNumber = 5;

  // 1~6 랜덤 숫자 생성
  int _generateRandomDiceNumber() => Random().nextInt(6) + 1;

  // 주사위 클릭 시 호출
  void _onDiceTap() {
    setState(() {
      _firstDiceNumber = _generateRandomDiceNumber();
      _secondDiceNumber = _generateRandomDiceNumber();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).colorScheme.primary,
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.primary,
        title: const Text('Dice App'),
      ),
      body: Center(
        child: Row(
          children: [
            // 주사위 이미지 빌드
            _buildDice(_firstDiceNumber),
            _buildDice(_secondDiceNumber),
          ],
        ),
      ),
    );
  }

  // 주사위 위젯 생성
  Widget _buildDice(int diceNumber) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: GestureDetector(
          onTap: _onDiceTap,
          child: Image.asset('images/dice$diceNumber.png'),
        ),
      ),
    );
  }
}

로그인 화면 코드 - 폼과 밸리데이션

import 'package:flutter/material.dart';

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

  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final _nameController = TextEditingController();
  final _emailController = TextEditingController();
  final _formKey = GlobalKey<FormState>();

  String _name = '로그인 해주세요';

  void _onLogin() {
    final form = _formKey.currentState;
    if (form?.validate() ?? false) {
      print('login 성공');
      setState(() {
        _name = '${_nameController.text} 님 로그인 하셨습니다.';
      });
    } else {
      print('login 실패');
    }
  }

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Please Login'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Container(
          decoration: const BoxDecoration(
            border: Border(
              bottom: BorderSide(
                color: Colors.red, // 테두리 색상
                width: 3, // 테두리 두께
              ),
              top: BorderSide(
                color: Colors.red, // 테두리 색상
                width: 3, // 테두리 두께
              ),
              left: BorderSide(
                color: Colors.red, // 테두리 색상
                width: 3, // 테두리 두께
              ),
              right: BorderSide(
                color: Colors.red, // 테두리 색상
                width: 3, // 테두리 두께
              ),
            ),
          ),
          child: Form(
            key: _formKey,
            child: Column(
              children: [
                TextFormField(
                  controller: _nameController,
                  decoration: const InputDecoration(labelText: '이름'),
                  validator: (text) => text!.isEmpty ? '이름을 입력해 주세요' : null,
                ),
                TextFormField(
                  controller: _emailController,
                  decoration: const InputDecoration(labelText: '이메일'),
                  validator: (text) {
                    if (text!.isEmpty) {
                      return '이메일을 입력해 주세요';
                    }
                    String pattern =
                        r'^[a-zA-Z0-9.a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$';
                    RegExp regex = RegExp(pattern);
                    if (!regex.hasMatch(text)) {
                      return '올바른 이메일을 입력해 주세요';
                    }
                    return null;
                  },
                ),
                const SizedBox(
                  height: 20,
                ),
                ElevatedButton(
                  onPressed: _onLogin,
                  child: const Text('로그인'),
                ),
                const SizedBox(
                  height: 20,
                ),
                Text(
                  _name,
                  style: Theme.of(context).textTheme.headlineMedium,
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

로티파일 사용