본문 바로가기
버킷리스트/앱 만들기

오공완 (with Do it! 플러터 앱 프로그램밍) #11

by 또또도전 2024. 5. 13.
반응형

// 이곳에 쓴 내용은 앱 만들기라는 버킷리스트를 달성하기 위해 플러터를 공부하면서 정리하고 있는 내용입니다.

플러터에 대해 아는 것이 거의 없기 때문에 정리하면서 오류가 있을 수 있습니다.

오류를 발견하신 분은 댓글 남겨 주시면 감사하겠습니다.

 

   애니메이션 구현하기

 

https://api.flutter.dev/flutter/animation/Curves-class.html

 

Curves class - animation library - Dart API

A collection of common animation curves. See also: Curve, the interface implemented by the constants available from the Curves class. Easing, for the Material animation curves. Constructors Curves() Properties hashCode → int The hash code for this object

api.flutter.dev

https://flutter-gallery-archive.web.app/

 

Flutter Gallery

Google uses cookies to deliver its services, to personalize ads, and to analyze traffic. You can adjust your privacy controls anytime in your Google settings. Learn more. Okay

flutter-gallery-archive.web.app

구글 스토어에서 앱을 다운 받을 수 있다는데 못 찾겠다. 웹으로 확인 가능하지만 뭘 확인해야 할지는 모르겠다.^^;;

 

오늘도 열심히 타자 연습을 했다.

눈이 빠지겠다. 어떤 순서가 있지만 그 순서들을 무시하고 타이핑을 했더니만 어떤 작동 원리인지는 잘 모르겠다.

그냥 이런 기능이 있구나 정도만 파악하고 있다.ㅜ.ㅜ

도대체 왜 state를 작성할 때 introPage 이딴 식으로 쓰는 걸까? 왜 가운데 글자를 대문자로 써야 하는거지?

이유가 없다면 정말 짜증이 난다. 이걸 찾으려고 도대체 몇 개의 다트 파일을 훑어 보고 또 봤는지..ㅜ.ㅜ

pubspec.yaml에 image 3개를 등록하고, 코드를 복사?했다. 이번에는 무조건 따라 쳤다. 무슨 의미냐면 stless를 치면 나오면 state 지정하는 문구 등을 그냥 똑같이 쳤고, 줄도 똑같이 했다. 그래야 오류 부분을 쉽게 찾을 수가 있어서..

 

그럼, 오늘의 타이핑 확인?

main.dart

import 'package:flutter/material.dart';

import 'intro.dart';
import 'people.dart';
import 'secondPage.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.blue),
        useMaterial3: true,
      ),
      home: const IntroPage(),
    );
  }
}

class AnimationApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AnimationApp();
}

class _AnimationApp extends State<AnimationApp>{

  List<People> peoples = List.empty(growable: true);
  int current  = 0;
  Color weightColor = Colors.blue;
  double _opacity = 1;

  @override
  void initState(){
    peoples.add(People('스미스', 180, 92));
    peoples.add(People('메리', 162, 55));
    peoples.add(People('존', 177, 75));
    peoples.add(People('바트', 130, 40));
    peoples.add(People('콘', 194, 140));
    peoples.add(People('디디', 100, 80));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Animatioin Example'),
      ),
      body: Container(
      child: Center(
      child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
      AnimatedOpacity(
      opacity: _opacity,
      duration: const Duration(seconds: 1),
      child:

      SizedBox(
          height: 200,
          child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              crossAxisAlignment: CrossAxisAlignment.end,
              children: <Widget>[
              SizedBox(width:100, child: Text('이름: ${peoples[current].name}')),
          AnimatedContainer(
            duration: const Duration(seconds: 2),
            curve: Curves.bounceIn,
            color: Colors.amber,
            width: 50,
            height: peoples[current].height,
            child: Text(
    '키${peoples[current].height}',
    textAlign: TextAlign.center,
            ),
    ),
    AnimatedContainer(
      duration: const Duration(seconds: 2),
      curve: Curves.easeInCubic,
      color: weightColor,
      width: 50,
      height: peoples[current].weight,
      child: Text(
    '몸무게${peoples[current].weight}',
    textAlign: TextAlign.center,
      ),
      ),
      AnimatedContainer(
      duration: const Duration(seconds: 2),
    curve: Curves.linear,
    color: Colors.pinkAccent,
    width: 50,
    height: peoples[current].bmi,
    child: Text(
    'bmi${peoples[current].bmi}',
    textAlign: TextAlign.center,
    ),
    )
      ],
    ),
    ),
    ),
    ElevatedButton(
    onPressed: (){
      setState((){
        if (current < peoples.length - 1){
    current++;
    }
        _changeWeightColor(peoples[current].weight);
    });
    },
    child: const Text('다음'),
    ),
    ElevatedButton(
    onPressed: () {
      setState((){
        if (current > 0){
          current--;
    }
        _changeWeightColor(peoples[current].weight);
    });
    },
    child: const Text('이전'),
    ),
    ElevatedButton(onPressed: () {
    setState((){
    _opacity == 1? _opacity = 0 : _opacity = 1;
    });
    },
    child: const Text('사라지기'),
    ),
    ElevatedButton(
    onPressed: (){
      Navigator.of(context).push(MaterialPageRoute(builder:
    (context)=> SecondPage()));
    },
    child: SizedBox(
    width: 200,
    child: Row(
    children: const <Widget>[
      Hero(tag: 'detail', child: Icon(Icons.cake)),
    Text('이동하기')
    ],
    ),
    ),),
    ],),),),
    );
  }
  void _changeWeightColor(double weight){
    if (weight < 40) {
      weightColor = Colors.blueAccent;
    } else if (weight < 60){
      weightColor = Colors.indigo;
    } else if (weight < 80){
      weightColor = Colors.orange;
    } else {
      weightColor = Colors.red;
    }
  }
}

intro.dart

import 'package:doit11_1/main.dart';
import 'package:flutter/material.dart';
import 'package:doit11_1/saturnLoading.dart';
import 'dart:async';

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

  @override
  State<StatefulWidget> createState() => _IntroPage();
}

class _IntroPage extends State<IntroPage> {
  @override
  void initState() {
    super.initState();
    loadData();
  }

  Future<Timer> loadData() async {
    return Timer(const Duration(seconds: 5), onDoneLoading);
  }

  onDoneLoading() async {
    Navigator.of(context).pushReplacement(
        MaterialPageRoute(builder: (context) => AnimationApp()));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('애니메이션 앱'),
            const SizedBox(
              height: 20,
            ),
            SaturnLoading()
          ],
        ),),);
  }
}

people.dart

class People{
  String name;
  double height;
  double weight;
  double? bmi;

  People(this.name, this.height, this.weight){
    bmi = weight / ((height/100)*(height/100));
  }
}

 

saturnLoading.dart

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

class SaturnLoading extends StatefulWidget {
  final _SaturnLoading _saturnLoading = _SaturnLoading();

  SaturnLoading({super.key});

  void start(){
    _saturnLoading.start();
  }

  void stop(){
    _saturnLoading.stop();
  }

  @override
  State<StatefulWidget> createState() => _SaturnLoading();
}

class _SaturnLoading extends State<SaturnLoading>
with SingleTickerProviderStateMixin {
  AnimationController? _animationController;
  Animation? _animation;

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _animationController!,
      builder: (context, child){
        return SizedBox(
          width: 100,
          height: 100,
          child: Stack(
            children: <Widget>[
              Image.asset(
                'repo/images/circle.png',
                width: 100,
                height: 100,
              ),
          Center(
            child: Image.asset(
              'repo/images/sunny.png',
              width: 30,
              height: 30,
            ),
        ),
              Padding(
                padding: const EdgeInsets.all(5),
                child: Transform.rotate(
                  angle: _animation!.value,
                  origin: const Offset(35, 35),
                  child: Image.asset(
                    'repo/images/saturn.png',
                    width: 20,
                    height: 20,
                  ),),)
            ],),);
      },);
  }

  void stop(){
    _animationController!.stop(canceled: true);
  }

  void start(){
    _animationController!.repeat();
  }

  @override
  void initState(){
    super.initState();
    _animationController=
    AnimationController(vsync: this, duration: const Duration(seconds: 3));
    _animation =
        Tween<double>(begin: 0, end: pi * 2).animate(_animationController!);
    _animationController!.repeat();
  }

  @override
  void dispose(){
    _animationController!.dispose();
    super.dispose();
  }
}

 

secondPage.dart

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

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

  @override
  State<StatefulWidget> createState() => _SecondPage();
}

class _SecondPage extends State<SecondPage>
    with SingleTickerProviderStateMixin {
  AnimationController? _animationController;
  Animation? _rotateAnimation;
  Animation? _scaleAnimation;
  Animation? _transAnimation;

  @override
  void initState() {
    super.initState();
    _animationController =
        AnimationController(duration: const Duration(seconds: 5), vsync: this);
    _rotateAnimation =
        Tween<double>(begin: 0, end: pi * 10).animate(_animationController!);
    _scaleAnimation =
        Tween<double>(begin: 1, end: 0).animate(_animationController!);
    _transAnimation =
        Tween<Offset>(begin: const Offset(0, 0), end: const Offset(200, 200))
            .animate(_animationController!);
  }

  @override
  void dispose() {
    _animationController!.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Animation Example2'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            AnimatedBuilder(
              animation: _rotateAnimation!,
              builder: (context, widget) {
                return Transform.translate(
                  offset: _transAnimation!.value,
                  child: Transform.rotate(
                      angle: _rotateAnimation!.value,
                      child: Transform.scale(
                        scale: _scaleAnimation!.value,
                        child: widget,
                      )),
                );
              },
              child: const Hero(
                  tag: 'detail',
                  child: Icon(
                    Icons.cake,
                    size: 300,
                  )),
            ),
            ElevatedButton(
              onPressed: () {
                _animationController!.forward();
              },
              child: const Text('로테이션 시작하기'),
            ),
          ],
        ),
      ),
    );
  }
}

sliverPage.dart

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

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

  @override
  State<StatefulWidget> createState() => _SliverPage();
}

class _SliverPage extends State<SliverPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            pinned: true,
            expandedHeight: 150,
            flexibleSpace: FlexibleSpaceBar(
              title: const Text('Sliver Example'),
              background: Image.asset('repo/image/sunny.png'),
            ),
            backgroundColor: Colors.deepOrangeAccent,
          ),
          SliverPersistentHeader(
            delegate: _HeaderDelegate(
              minHeight: 50,
              maxHeight: 150,
              child: Container(
                color: Colors.blue,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const <Widget>[
                      Text(
                        'list 숫자',
                        style: TextStyle(fontSize: 30),
                      ),
                    ],
                  ),
                ),
              )),
            pinned: true,
          ),
          SliverList(
            delegate: SliverChildListDelegate([
              customCard('1'),
              customCard('2'),
              customCard('3'),
              customCard('4'),
            ])),
          SliverPersistentHeader(
            delegate: _HeaderDelegate(
              minHeight: 50,
              maxHeight: 150,
              child: Container(
                color: Colors.blue,
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                      children: const<Widget>[
                        Text(
                          '그리드 숫자',
                          style: TextStyle(fontSize: 30),
                        ),
                      ],
                  ),
                ),
              )),
            pinned: true,
          ),
          SliverGrid(
            delegate: SliverChildListDelegate([
              customCard('1'),
              customCard('2'),
              customCard('3'),
              customCard('4'),
            ]),
            gridDelegate:
              const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2)),
          ],
      ),
    );
  }

  Widget customCard(String text){
    return Card(
      child: Container(
        height: 120,
        child: Center(
          child: Text(
            text,
            style: const TextStyle(fontSize: 40),
          )),
      ),
    );
  }
}

class _HeaderDelegate extends SliverPersistentHeaderDelegate {
  final double minHeight;
  final double maxHeight;
  final Widget child;

  _HeaderDelegate({
    required this.minHeight,
    required this.maxHeight,
    required this.child,
  });

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContexnt){
    return SizedBox.expand(child: child);
  }

  @override
  double get maxExtent => math.max(maxHeight, minHeight);

  @override
  double get minExtent => minHeight;

  @override
  bool shouldRebuild(_HeaderDelegate oldDelegate){
    return maxHeight != oldDelegate.maxHeight ||
    minHeight != oldDelegate.minHeight ||
    child != oldDelegate.child;
  }
}

 

오늘은 별다른 코멘트를 붙이고 싶지 않다. 너무 힘들었다. 타이핑하는 것만으로도..

이해는 언젠가 되겠지~^^;;

반응형