for, for in, foreach, while문

List<type>.generate

void basicLoop(){
  List<String> rainbow = ['1','2','3','4','5','6'];

  for(int i=0; i<rainbow.length; i++){
    print(rainbow[i]);
  }

  for(String x in rainbow){
    print(x);
  }

  rainbow.forEach((name){
    print(name);
  });
}

void main(){
  basicLoop();
  return;
}

 

[로또번호 생성기 ver_1]

아래 프로그램은 list자료형의 특성상 random으로 값을 생성할 때 중복된 값을 허용함

import 'dart:math';

List<int> lottoNmber(){
  var random = Random();
  List<int> lottoList = [];
  var num;

  for(int i=0; i<6; i++){
    num = random.nextInt(45) + 1;
    lottoList.add(num);
  }

  print('당첨번호');
  print(lottoList);

  return lottoList;
}

List<int> myNumber(){
  var random = Random();
  List<int> myList = [];
  var num;

  for(int i=0; i<6; i++){
    num = random.nextInt(45) + 1;
    myList.add(num);
  }

  print('내 추첨번호');
  print(myList);

  return myList;
}

void checkNumber(lottoList, myList){

  int match = 0;

  for(int lotto in lottoList){
    for(int myNum in myList){
      if(lotto == myNum){
        match++;
        print('당첨번호: $lotto');
      }
      //print("로또번호 = $lotto");
      //print("내 추첨번호 = $myNum");
    }
  }
  print("$match개의 당첨번호가 있습니다.");
}

void main(){
  List<int>lottoFinal = lottoNmber();
  List<int>myFinal = myNumber();

  checkNumber(lottoFinal, myFinal);

  return;
}

 

[로또번호 생성기 ver_2]

set을 사용해서 중복된 값 제외하기

import 'dart:math';

Set<int> lottoNmber(){
  final random = Random();
  final Set<int> lottoSet = {};

  while(lottoSet.length != 6){
    lottoSet.add(random.nextInt(45)+1);
  }
  print('당첨번호');
  print(lottoSet.toList());

  return lottoSet;
}

Set<int> myNumber(){
  final random = Random();
  final Set<int> mySet = {};

  while(mySet.length != 6){
    mySet.add(random.nextInt(45)+1);
  }

  print('내 추첨번호');
  print(mySet.toList());
  return mySet;
}

void checkNumber(lottoSet, mySet){

  int match = 0;

  for(int lotto in lottoSet){
    for(int myNum in mySet){
      if(lotto == myNum){
        match++;
        print('당첨번호: $lotto');
      }
      //print("로또번호 = $lotto");
      //print("내 추첨번호 = $myNum");
    }
  }
  print("$match개의 당첨번호가 있습니다.");
}

void main(){
  Set<int>lottoFinal = lottoNmber();
  Set<int>myFinal = myNumber();

  checkNumber(lottoFinal, myFinal);

  return;
}

 

리스트 제너레이터

List<int>.generate(45, (i) => i+1);

예시

void main(){
  var text = List<int>.generate(45, (i) => i+1);
  print(text);
}

 

 

dart언어 cascade notation

class Person{
  String? name;
  int? age;

  void set(int x){
    age = x;
  }

  void show(){
    print(name);
    print(age);
  }
}

void main(){
  Person p1 = Person();
  p1.set(30);
  p1.name = "Lee";
  p1.show();

  Person p2 = Person();
  p2..set(20)..name = "Kim"..show();

  Person p3 = Person();
  p3..set(40)
    ..name="Park"
    ..show();
}

 

cascade 표기법을 활용해서 랜덤한 lotto 숫자 6개 뽑아내기

void main(){
  var test = (List<int>.generate(45, (i) => i+1)..shuffle()).sublist(0,6);
  print(test);
}

 

다시 로또프로그램 만들기

 

코드 리팩토링이란?

코드를 유지 보수하기 좋게 바꾸는 것

가독성도 높일 수 있고 불필요한 중복을 피할 수 있다.
이는 결국 다른 사람과의 협업에도 도움이 된다.

 

참고: https://velog.io/@qkrtnfks128/Flutter-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81refactoring

 

[Flutter] 코드 리팩토링(refactoring)

코드를 간결하게~~!!

velog.io

 

 

 

로그인 페이지를 통한 코드 리팩토링

main.dart

import 'package:flutter/material.dart';
import 'login_app/login.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Firebase login app',
      home: LogIn(),
    );
  }
}

 

my_button.dart

유사한 모양의 버튼을 여러개 사용하는 상황에서 

MyButton이라는 클래스를 만들어서 변경요소인 image, text, color, radius, VoidCallback 함수 를 매개변수로 생성자를 호출하면 동일한 버튼을 찍어낼 수 있도록 함.

required 문법에 대한 이해 필요

import 'package:coderefactoring/login_app/login.dart';
import 'package:flutter/material.dart';

class MyButton extends StatelessWidget {
  MyButton({Key? key, required this.image, required this.text, required this.color, required this.radius, required this.onPressed}) : super(key: key);

  final Widget image;
  final Widget text;
  final Color color;
  final double radius;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {

    return ButtonTheme(
      height: 50.0,
      child: ElevatedButton(
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            image,
            text,
            Opacity(
              opacity: 0.0,
              child: Image.asset('images/glogo.png'),
            ),
          ],
        ),
        style: ElevatedButton.styleFrom(
            backgroundColor: color
        ),
        onPressed: onPressed,
      ),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.all(
          Radius.circular(radius),
        ),
      ),
    );
  }
}

 

 

login.dart

MyButton객체를 이용해서 버튼을 쉽게 생성

import 'package:coderefactoring/my_button/my_button.dart';
import 'package:flutter/material.dart';

class LogIn extends StatelessWidget {
  const LogIn({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blue,
        title: Text(
          'Sign In',
          style: TextStyle(color: Colors.white),
        ),
        centerTitle: true,
        elevation: 0.2,
      ),
      body: buildButton(),
    );
  }

  Widget buildButton() {
    return Padding(
      padding: EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MyButton(
              image: Image.asset('images/glogo.png'),
              text: Text(
                'Login with Google',
                style: TextStyle(color: Colors.black87, fontSize: 15.0),
              ),
              color: Colors.white,
              radius: 4.0,
              onPressed: () {}),
          SizedBox(
            height: 10.0,
          ),
          MyButton(
              image: Image.asset('images/flogo.png'),
              text: Text(
                'Login with Facebook',
                style: TextStyle(color: Colors.white, fontSize: 15.0),
              ),
              color: Color(0xFF334D92),
              radius: 4.0,
              onPressed: () {}),
          SizedBox(
            height: 10.0,
          ),
          MyButton(
              image: Icon(
                Icons.mail,
                color: Colors.white,
              ),
              text: Text(
                'Login with Email',
                style: TextStyle(color: Colors.white, fontSize: 15.0),
              ),
              radius: 4.0,
              color: Colors.green,
              onPressed: () {},
          ),

          SizedBox(
            height: 10.0,
          ),
        ],
      ),
    );
  }
}

 

 

1. main.dart에서 우리가 눈여겨 볼 것은 총 3가지이다.

  가. 페이지이동

 

  나. Textfield 컨트롤

다. 스낵바 사용하기

 - 영상강좌에서는 SnackBar를 사용하기 위해 build위젯으로 감싸서 context를 만들어주는 부분이 있다 하지만 개선된 버전에서는 build위젯으로 감싸서 context를 만들어주는 부분이 필요없다.

빌더위젯으로 감싸지 않고 바로 처리
스낵바 구현 부분

 

 

main.dart

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Dice game',
      home: LogIn(),
    );
  }
}

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

  @override
  State<LogIn> createState() => _LogInState();
}

class _LogInState extends State<LogIn> {
  TextEditingController controller = TextEditingController();
  TextEditingController controller2 = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Log in'),
          backgroundColor: Colors.redAccent,
          centerTitle: true,
          leading: IconButton(
            icon: Icon(Icons.menu),
            onPressed: () {},
          ),
          actions: [IconButton(onPressed: () {}, icon: Icon(Icons.menu))],
        ),
        body: GestureDetector(
          onTap: (){
            FocusScope.of(context).unfocus(); //키보드 없애기
          },
          child: SingleChildScrollView(
                //스크롤 뷰로 구성
                child: Column(
                  children: [
                    Padding(padding: EdgeInsets.only(top: 50)),
                    Center(
                        child: Image(
                      image: AssetImage('image/chef.gif'),
                      width: 170.0,
                      height: 190.0,
                    )),
                    Form(
                        child: Theme(
                            data: ThemeData(
                                primaryColor: Colors.teal,
                                inputDecorationTheme: InputDecorationTheme(
                                    labelStyle: TextStyle(
                                        color: Colors.teal, fontSize: 15.0))),
                            child: Container(
                              padding: EdgeInsets.all(40.0),
                              child: Column(
                                children: [
                                  TextField(
                                    autofocus: true,
                                    controller: controller,
                                    decoration: InputDecoration(
                                        labelText: 'Enter "dice"'),
                                    keyboardType: TextInputType.emailAddress,
                                  ),
                                  TextField(
                                    controller: controller2,
                                    decoration: InputDecoration(
                                        labelText: 'Enter Password'),
                                    keyboardType: TextInputType.text,
                                    obscureText: true,
                                  ),
                                  SizedBox(
                                    height: 40,
                                  ),
                                  ButtonTheme(
                                      minWidth: 100,
                                      height: 50,
                                      child: ElevatedButton(
                                          style: ElevatedButton.styleFrom(
                                              backgroundColor:
                                                  Colors.orangeAccent),
                                          onPressed: () {
                                            if (controller.text == 'dice' &&
                                                controller2.text == '1234') {
                                              Navigator.push(
                                                  context,
                                                  MaterialPageRoute(
                                                      builder: (BuildContext
                                                              context) =>
                                                          Dice())); //순한맛22번강좌
                                            } else if (controller.text !=
                                                    'dice' &&
                                                controller2.text == '1234') {
                                              showSnackBar2(context);
                                            } else if (controller.text ==
                                                    'dice' &&
                                                controller2.text != '1234') {
                                              showSnackBar3(context);
                                            } else {
                                              showSnackBar(context);
                                            }
                                          },
                                          child: Icon(Icons.arrow_forward,
                                              color: Colors.white, size: 35.0)))
                                ],
                              ),
                            )))
                  ],
                ),
              ),
        )
        );
  }
}

void showSnackBar(BuildContext context) {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: Text(
      '로그인 정보를 다시 확인하세요.',
      textAlign: TextAlign.center,
    ),
    duration: Duration(seconds: 2),
    backgroundColor: Colors.blue,
  ));
}

void showSnackBar2(BuildContext context) {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: Text(
      '비밀번호가 일치하지 않습니다.',
      textAlign: TextAlign.center,
    ),
    duration: Duration(seconds: 2),
    backgroundColor: Colors.blue,
  ));
}

void showSnackBar3(BuildContext context) {
  ScaffoldMessenger.of(context).showSnackBar(SnackBar(
    content: Text(
      'dice의 철자를 확인하세요.',
      textAlign: TextAlign.center,
    ),
    duration: Duration(seconds: 2),
    backgroundColor: Colors.blue,
  ));
}

dice.dart

- fluttertoast사용

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

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

  @override
  State<Dice> createState() => _DiceState();
}

class _DiceState extends State<Dice> {
  int leftDice = 1;
  int rightDice = 2;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.redAccent,
      appBar: AppBar(
        backgroundColor: Colors.redAccent,
        title: Text('Dice Game'),
      ),
      body: Center(
        //center위젯 안에서 Column이나 Row를 사용할 때
        //중앙 정렬하려면 MainAxisAlignment.center사용
        //순한맛강좌 21강
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Padding(
              padding: const EdgeInsets.all(32),
              child: Row(
                children: [
                  //Expanded위젯은 컬럼이면 세로로 로우면 가로로 꽉 채움
                  Expanded(child: Image.asset('image/dice$leftDice.png')),
                  SizedBox(
                    width: 20,
                  ),
                  Expanded(child: Image.asset('image/dice$rightDice.png')),
                ],
              ),
            ),
            SizedBox(
              height: 60,
            ),
            ButtonTheme(
              child: ElevatedButton.icon(
                style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.orangeAccent,
                    minimumSize: Size(100, 60)),
                onPressed: () {
                  setState(() {
                    leftDice = Random().nextInt(6)+1;
                    rightDice = Random().nextInt(6)+1;
                  });
                  showToast("Left dice: {$leftDice}, Right dice: {$rightDice}");
                },
                icon: Icon(Icons.play_arrow),
                label: Text("Next"),
              ),
            )
          ],
        ),
      ),
    );
  }
}

void showToast(String message){
  Fluttertoast.showToast(msg:message,
    backgroundColor: Colors.white,
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
  );
}

 

 

확인할 것

- setState사용법 확인

- FloatingActionButton은 여러개 표현할 때 Stack으로 표현

 

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(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void _decrementCounter(){
    setState(() {
      _counter--;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),

      ),
      floatingActionButton: Stack(
        children:[
          Align(
            alignment: Alignment(
              Alignment.center.x-0.15, Alignment.center.y + 0.35
            ),
            child: FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: const Icon(Icons.add),
            ),
          ),
          Align(
            alignment: Alignment(
                Alignment.center.x+0.4, Alignment.center.y + 0.35
            ),
            child: FloatingActionButton(
              onPressed: _decrementCounter,
              tooltip: 'Decrement',
              child: const Icon(Icons.remove),
            )
          )
        ],
      )
    );
  }
}

 

상속 문법(생성자에 유의해서 보길)

class Person{
  String? name;
  int? money;
  Person(this.name, this.money){}

  void status(){
    print("이름: $name , 골드: $money");
  }
}

class Hero extends Person{
  int? hp;
  int? mp;
  Hero(this.hp, this.mp, super.name, super.money);
  //생성자에서 중괄호 안에 내용이 없을 경우 생략해도 됨
  //단 생략할 경우 위 예시처럼 세미콜론 필수
  @override
  void status(){
    print("이름: $name , 골드: $money");
    print("hp: $hp , gold: $mp");
  }
}

class Magician extends Hero{
  String? skill;
  int? damage;
  Magician(this.skill, this.damage, super.hp, super.mp, super.name, super.money){}

  void magic(){
    print("Magic: $skill ,Dagmage: $damage!!");
  }
}


void main(){
  Person p1 = Person("Tim", 100);
  p1.status();
  Hero h1 = Hero(1,2,"Tim",100);
  h1.status();
  Magician m1 = Magician("Meteor", 999, 10,30,"Harry", 999);
  m1.status();
  m1.magic();
  m1.magic();
  m1.magic();
  m1.magic();
}

 

아래는 옛날 방식

class DialPhone{
  int? year;
  DialPhone(){
    print('이 전화기는 다이얼을 돌려서 전화를 겁니다.');
  }

  void whenCame(){
    print("1889년 발명");
  }
}

class ButtonPhone extends DialPhone{
  ButtonPhone(){
    print("이 전화기는 버튼을 눌러서 전화를 겁니다.");
  }

  @override
  void whenCame(){
    print("1963년 발명");
  }
}

class SmartPhone extends ButtonPhone{
  String? manufacturer;
  String? model;

  SmartPhone(String manufacturer, String model, int year){
    this.manufacturer = manufacturer;
    this.model = model;
    this.year = year;

    print("이 전화기는 터치를 해서 전화를 겁니다.");
  }

  @override
  void whenCame(){
    print("1993년 처음 등장");
  }
}

void main(){
  SmartPhone s1 = SmartPhone("제조사:삼성", "모델명:겔럭시 S20", 2020);
  print(s1.manufacturer.toString() + s1.model.toString() + s1.year.toString());

  s1.whenCame();
}

 

 

이번에는 유튜브 모바일과 데스크톱의 레이아웃이 다른 것처럼 반응형으로 웹앱의 Layout을 만드는 강의였다.

 

 

main.dart

import 'package:flutter/material.dart';
import 'my_page.dart';
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,

      ),
      home: MyPage(),
    );
  }
}

 

my_page.dart

- body를 ResponsiveLayout 위젯을 만들어 반응형으로 화면 구성

- ResponsiveLayout은 생성자에서 2개의 변수를 초기화 해야하므로 mobileBody, desktopBody를 매개변수로 넘겨줌

import 'package:flutter/material.dart';
import 'package:mild_2_youtube/responsive/desktop_body.dart';
import 'package:mild_2_youtube/responsive/mobile_body.dart';
import 'package:mild_2_youtube/responsive/responsive.dart';

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

  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: ResponsiveLayout(
        mobileBody: MobileBody(),
        desktopBody: DesktopBody(),
      ),
    );
  }
}

 

 

responsive.dart

- LayoutBuilder클래스를 사용해서 화면너비에 따라 모바일, 데스크탑용으로 화면layout을 바꿔주기 위해 사용

참고: https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html

- 제약조건을 거는데 최대 너비가 800이하일 경우 모바일, 이상일 경우 데스크탑 위젯 화면 보여줌

import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  const ResponsiveLayout(
      {Key? key, required this.mobileBody, required this.desktopBody})
      : super(key: key);

  final Widget mobileBody;
  final Widget desktopBody;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(builder: (context, constraints) {
      if (constraints.maxWidth < 800) {
        return mobileBody;
      } else {
        return desktopBody;
      }
    }); //기기 전체가 아니라 위젯의 크기를 알아내기
  }
}

 

mobile_body.dart

- 모바일 화면구성

- 컬럼 내에서 listview를 사용할 때 expanded를 감싸서 공간의 범위를 제한해줌

import 'package:flutter/material.dart';


class MobileBody extends StatelessWidget {
  const MobileBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Mobile'),
      ),
      backgroundColor: Colors.deepPurple,
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Container(
              height: 300,
              color: Colors.deepPurple[400],
            ),
          ),
          Expanded( //컬럼 내에서 Listview사용할 때는 Expanded로 감싸줘야함, 안그러면 container의 height를 쓸 수 없음
            child: ListView.builder(
                itemCount: 10,
                itemBuilder: (context, index){
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      color: Colors.deepPurple[300],
                      height: 120,
                    ),
                  );
                }),
          )
        ],
      ),
    );
  }
}

 

 

desktop_body.dart

- sidePanel의 너비를 300으로 고정하고 나머지는 너비를 주지않았고 리스트뷰를 expanded로 감쌈으로 해서 화면 크기 변화에 따라 1열의 너비만 변하는 것을 볼 수 있다.

import 'package:flutter/material.dart';

class DesktopBody extends StatelessWidget {
  const DesktopBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Desktop'),
      ),
      backgroundColor: Colors.deepPurple[200],
      body: Row(
        children: [
          Expanded(
            child: Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Container(
                    height: 300,
                    color: Colors.deepPurple[400],
                  ),
                ),
                Expanded( //컬럼 내에서 Listview사용할 때는 Expanded로 감싸줘야함, 안그러면 container의 height를 쓸 수 없음
                  child: ListView.builder(
                      itemCount: 10,
                      itemBuilder: (context, index){
                        return Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Container(
                            color: Colors.deepPurple[300],
                            height: 120,
                          ),
                        );
                      }),
                ),
              ],
            ),
          ),
          //side Panel
          Container(
            width: 300,
            child: ListView.builder(
                itemCount: 10,
                itemBuilder: (context, index){
                  return Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      color: Colors.deepPurple[300],
                      height: 120,
                    ),
                  );
                }),
          )
        ],
      ),
    );
  }
}

 

 

 

 

요약 - 리스트 아이템을 선택하면 동물페이지가 뜨고 좋아요를 누를 수 있게 됨

주요 기능

 

 

main.dart

- 메인 페이지이며 리스트 제너레이터 사용법 확인

- navigator를 통해 animal_page로 넘어갈 때 animalData를 넘겨줌

import 'package:flutter/material.dart';
import 'package:mild_2_listview/animal_page.dart';
import 'package:mild_2_listview/model.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyPage(),
    );
  }
}

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

  @override
  State<MyPage> createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  static List<String> animalName = [
    'Bear',
    'Camel',
    'Deer',
    'Fox',
    'Kangaroo',
    'Koala',
    'Lion',
    'Tiger'
  ];

  static List<String> animalImagePath = [
    'image/bear.png',
    'image/camel.png',
    'image/deer.png',
    'image/fox.png',
    'image/kangaroo.png',
    'image/koala.png',
    'image/lion.png',
    'image/tiger.png'
  ];

  static List<String> animalLocation = [
    'forest and mountain',
    'dessert',
    'forest',
    'snow mountain',
    'Australia',
    'Australia',
    'Africa',
    'Korea'
  ];

  final List<Animal> animalData = List.generate(
      animalLocation.length,
      (index) => Animal(
          animalName[index], animalImagePath[index], animalLocation[index]));

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("ListView"),
        ),
        body: ListView.builder(
            itemCount: animalData.length,
            itemBuilder: (context, index) {
              //debugPrint(index.toString()+" : "+animalData[index].imgPath.toString());
              return Card(
                  child: ListTile(
                      title: Text(animalData[index].name),
                      leading: SizedBox(
                        height: 50,
                        width: 50,
                        child: Image.asset(animalData[index].imgPath),
                      ),
                    onTap: (){
                        Navigator.of(context).push(MaterialPageRoute(
                            builder: (context)=>AnimalPage(animal: animalData[index],)));
                        debugPrint(animalData[index].name);
                    },
                  ));
            }));
  }
}

 

animal_page.dart

- 넘겨받음 animal데이터로 page구현

- like_button패키기 사용(https://pub.dev/packages/like_button/install)

import 'package:flutter/material.dart';
import 'package:mild_2_listview/model.dart';
import 'package:like_button/like_button.dart';

class AnimalPage extends StatefulWidget {
  const AnimalPage({Key? key, required this.animal}) : super(key: key);
  //null 값을 가질 수 없는 this.animal에는 required를 붙여준다
  final Animal animal;

  @override
  State<AnimalPage> createState() => _AnimalPageState();
}

class _AnimalPageState extends State<AnimalPage> {
  bool isLiked = false;
  int likeCount = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.animal.name),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SizedBox(
                height: 200, width: 200, child: Image.asset(widget.animal.imgPath)),
            const SizedBox(
              height: 20,
            ),
            Text('It lives in ' + widget.animal.location,
            style: const TextStyle(
              fontSize: 18
            ),),
            const SizedBox(
              height: 20,
            ),
            LikeButton(
              size: 30,
                isLiked: isLiked,
                likeCount: likeCount,
            )
          ],
        ),
      ),
    );
  }
}

 

model.dart

- Animal클래스

import 'package:flutter/material.dart';

class Animal{
  final String name;  //final 은 runtime시에 값이 정해질 수 있기 때문에
  final String imgPath;
  final String location;

  Animal(this.name, this.imgPath, this.location);
}

 

 

 

1. Alt + Enter - 라이브러리 추가 등

2. Shift + Enter - 다음칸으로 커서 이동

+ Recent posts