티스토리 뷰

플러터

도서 목록 앱

하몬드 2023. 7. 31. 14:47

프로젝트 요구사항

 

 

위처럼 크게 3가지 요소로 요구사항을 정리할 수 있다.

 

 

전체 목록 화면 구현

 

폴더구조

 

list_screen: 이미지와 책제목이 나열된 도서목록을 출력하는 화면

detail_screen: 책을 클릭하면 나오는 해당 책에 관한 세부정보를 출력하는 화면

 

 

화면 확인

 

import 'package:flutter/material.dart';
import 'package:flutter_application_1/screens/list_screen.dart';

void main() => runApp(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, fontFamily: 'OpenSans'),
        home: ListScreen()); // 여기다 클래스명 추가
  }
}

 

main.dart에서 MaterialApp의 home프로퍼티에

다른 dart파일에서 만들어놓은 클래스명을 적으면 구성한 화면 확인이 가능하다.

시작화면을 설정하는 거라 보면 된다.

 

 

ListView

 

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('도서 목록 앱'),
      ),
      body: ListView(
        children: [
          ListTile( // ListView의 구성요소(한 줄) 
            title: const Text('한 권이면 충분해, 프로툴스'),
            leading: Image.network(''), // 맨 앞에 나타나는 위젯
          )
        ],
      ),
    );
  }
}

 

ListView를 통해 도서목록을 생성했다.

ListView에 ListTile을 children에 추가해 목록을 꾸밀 수 있다.

 

 

이미지 주소 추가

 

Image.network('이미지주소')를 통해 이미지를 불러올 수 있다.

 

 

비제이퍼블릭

 

bjpublic.tistory.com

도서 정보는 여기서 가져오면 된다.

 

 

 

여기 있는 책들 중 아무거나 선택한 후

 

 

 

책 세부 정보 화면에 있는 책 이미지를 우클릭(control + 클릭)하여 '이미지 주소 복사' 선택

 

 

leading: Image.network('')

 

이 안에다 주소를 복붙 한다.

 

 

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('도서 목록 앱'),
      ),
      body: ListView(
        children: <Widget>[
          ListTile(
            title: const Text('한 권이면 충분해, 프로툴스'),
            leading: Image.network(
                'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBSOU%2FbtrNb9iQ2X4%2FBtNV0ZUfJeLaUBUKckf3gK%2Fimg.jpg'), 
          ),
          ListTile(
            title: const Text('부동산 공매가 답이다'),
            leading: Image.network(
                'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSGHYz%2FbtrM1exYBGl%2Ff8jkIc0ERexQGNW2HL2kAk%2Fimg.jpg'), 
          ),
          ListTile(
            title: const Text('예제부터 배우는 거꾸로 파이썬'),
            leading: Image.network(
                'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbryLDA%2FbtrIMBW3FAB%2FeFTxvdukNYKr4X8TmtpDik%2Fimg.jpg'),
          ),
          ListTile( 
            title: const Text('한 번에 붙는 SQLD'),
            leading: Image.network(
                'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkewgB%2FbtrFmXb6h4Q%2F8tWAPSroS7IZq6P95Z3780%2Fimg.jpg'), 
          )
        ],
      ),
    );
  }
}

 

ListTile을 4개 정도 만들었다.

 

 

BookTile 클래스 추가

 

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('도서 목록 앱'),
      ),
      body: ListView(
        children: <Widget>[
          BookTile(
              title: '한 권이면 충분해, 프로툴스',
              subtitle: '프로툴스 레코딩, 에디팅, 믹싱, 미디까지 한 권으로 끝내자',
              description:
                  '본서는 프로툴스의 설치 방법과 기본적인 사용법부터 레코딩과 믹싱 등의 다양한 기능, 그리고 새롭게 추가된 기능들을 중심으로 내용을 구성했습니다. 음악 작업 시에 사용되지 않는 불필요한 내용들은 과감하게 생략하고 실제 작업에 꼭 필요한 부분만을 다루었습니다. 또한 단순히 프로툴스라는 DAW의 설명에서 벗어나 실제 워크 플로우에 맞게 배울 수 있기 때문에 프로툴스를 처음 사용하는 사람, 더 나아가 엔지니어 지망생, 프로듀서, 싱어송라이터에게 특히 유용한 책이 될 것입니다.',
              image:
                  'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBSOU%2FbtrNb9iQ2X4%2FBtNV0ZUfJeLaUBUKckf3gK%2Fimg.jpg'),
        ],
      ),
    );
  }
}

class BookTile extends StatelessWidget {
  final String title;
  final String subtitle;
  final String description;
  final String image;
  BookTile({
    required this.title,
    required this.subtitle,
    required this.description,
    required this.image,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      leading: Image.network(image),
      onTap: () {},
    );
  }
}

 

앞서 방식대로 구현하면 ListTile을 클릭하고 넘어가는 세부화면에서

subtitle, description 등의 전달해야 하는 데이터들을 ListTitle이 가지고 있을 수가 없다.

 

그래서 'BookTile'이라는 책의 정보를 담고 있으며,

ListTile을 반환하는 위젯을 만들었다. 

 

4개의 문자열 변수를 선언한 후 각 매개변수를

required 키워드와 함께 생성자를 정의하여

변수에는 필수적으로 값이 들어가야 한다. 

 

onTap의 내용엔 BookTile(ListTile)을 눌렀을 때,

책의 세부정보로 이동하는 기능을 하는 함수가 들어갈 것이다.

 

 

세부 정보 화면 구현

 

Ui 구현

 

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('한 권이면 충분해, 프로툴스'),
      ),
      body: Column(
        children: [
          Image.network(
              'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBSOU%2FbtrNb9iQ2X4%2FBtNV0ZUfJeLaUBUKckf3gK%2Fimg.jpg'),
          const Padding(padding: EdgeInsets.all(3)),
          Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Container(
                  width: MediaQuery.of(context).size.width * 0.8, // overflow 방지
                  padding: const EdgeInsets.all(10),
                  child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Container(
                          child: const Text(
                            '한 권이면 충분해, 프로툴스',
                            style: TextStyle(
                                fontSize: 23, fontWeight: FontWeight.bold),
                          ),
                        ),
                        const Text(
                          '프로툴스 레코딩, 에디팅, 믹싱, 미디까지 한 권으로 끝내자',
                          style: TextStyle(fontSize: 18, color: Colors.grey),
                        )
                      ]),
                ),
                Container(
                  width: MediaQuery.of(context).size.width * 0.15,
                  padding: const EdgeInsets.all(10),
                  child: const Center(
                      child: Icon(
                    Icons.star,
                    color: Colors.red,
                  )),
                ),
              ]),
          const Padding(padding: EdgeInsets.all(3)),
          // 기능은 없지만 허전해서 넣는 아이콘들
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Column(
                children: [
                  Icon(
                    Icons.call,
                    color: Colors.blue,
                  ),
                  Text('Contact', style: TextStyle(color: Colors.blue)),
                ],
              ),
              Column(
                children: [
                  Icon(
                    Icons.near_me,
                    color: Colors.blue,
                  ),
                  Text('Route', style: TextStyle(color: Colors.blue)),
                ],
              ),
              Column(
                children: [
                  Icon(
                    Icons.save,
                    color: Colors.blue,
                  ),
                  Text('Save', style: TextStyle(color: Colors.blue)),
                ],
              ),
            ],
          ),
          Container(
            padding: const EdgeInsets.all(15),
            child: const Text(
                '본서는 프로툴스의 설치 방법과 기본적인 사용법부터 레코딩과 믹싱 등의 다양한 기능, 그리고 새롭게 추가된 기능들을 중심으로 내용을 구성했습니다. 음악 작업 시에 사용되지 않는 불필요한 내용들은 과감하게 생략하고 실제 작업에 꼭 필요한 부분만을 다루었습니다. 또한 단순히 프로툴스라는 DAW의 설명에서 벗어나 실제 워크 플로우에 맞게 배울 수 있기 때문에 프로툴스를 처음 사용하는 사람, 더 나아가 엔지니어 지망생, 프로듀서, 싱어송라이터에게 특히 유용한 책이 될 것입니다.'),
          )
        ],
      ),
    );
  }
}

 

Column내에서 사진, 제목, 부제목, 아이콘, 설명 등을 한 줄로 배치시켰다.

 

 

 

 

기능 구현

 

구현할 기능들

 

1. ListScreen에서 DetailScreen으로 이동하는 코드 작성

2. DetailScreen에서 ListScreen으로 부터 받아올 데이터 선언

3. DetailScreen으로 이동하는 코드에 데이터 넘겨주기

 

 

화면 이동 구현

 

class BookTile extends StatelessWidget {
  final String title;
  final String subtitle;
  final String description;
  final String image;
  const BookTile({
    super.key,
    required this.title,
    required this.subtitle,
    required this.description,
    required this.image,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      leading: Image.network(image),
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => DetailScreen(
              title: title,
              subtitle: subtitle,
              description: description,
              image: image,
            ),
          ),
        );
      },
    );
  }
}

 

onTap에서 Navigator를 이용해 ListScreen에서 DetailScreen으로 화면 이동을 하는 코드 작성,

title, subtitle, description, image 값들을 DetailScreen으로 넘겨준다.

 

 

데이터 받아오기

 

import 'package:flutter/material.dart';

class DetailScreen extends StatelessWidget {
  final String title;
  final String subtitle;
  final String description;
  final String image;
  const DetailScreen({
    super.key,
    required this.title,
    required this.subtitle,
    required this.description,
    required this.image,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: [
          Image.network(image),
          const Padding(padding: EdgeInsets.all(3)),
          Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Container(
                  width: MediaQuery.of(context).size.width * 0.8, // overflow 방지
                  padding: const EdgeInsets.all(10),
                  child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Container(
                          child: Text(
                            title,
                            style: const TextStyle(
                                fontSize: 23, fontWeight: FontWeight.bold),
                          ),
                        ),
                        Text(
                          subtitle,
                          style:
                              const TextStyle(fontSize: 18, color: Colors.grey),
                        )
                      ]),
                ),
                Container(
                  width: MediaQuery.of(context).size.width * 0.15,
                  padding: const EdgeInsets.all(10),
                  child: const Center(
                      child: Icon(
                    Icons.star,
                    color: Colors.red,
                  )),
                ),
              ]),
          const Padding(padding: EdgeInsets.all(3)),
          // 기능은 없지만 허전해서 넣는 아이콘들
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Column(
                children: [
                  Icon(
                    Icons.call,
                    color: Colors.blue,
                  ),
                  Text('Contact', style: TextStyle(color: Colors.blue)),
                ],
              ),
              Column(
                children: [
                  Icon(
                    Icons.near_me,
                    color: Colors.blue,
                  ),
                  Text('Route', style: TextStyle(color: Colors.blue)),
                ],
              ),
              Column(
                children: [
                  Icon(
                    Icons.save,
                    color: Colors.blue,
                  ),
                  Text('Save', style: TextStyle(color: Colors.blue)),
                ],
              ),
            ],
          ),
          Container(
            padding: const EdgeInsets.all(15),
            child: Text(description),
          )
        ],
      ),
    );
  }
}

 

ListScreen으로부터 받아온 변수값들의 필드를 추가해 변수값이 화면에 출력되도록 했다.

 

Ui를 구성하면서 작성했던 예제코드에서 작성했던

제목, 부제목, 설명, 이미지주소를 선언한 변수명으로 바꿔주면 된다.

 

 

실행화면

 

 

 

구조 개선

 

도서 목록 앱을 완성했지만 더미 데이터와 UI코드와 함께 있어 전체코드가 지저분하고 읽기 어렵다.

그래서 UI와 데이터 영역을 구분할 것이다.

 

 

할 거 요약

 

1. Book 모델 만들기

2. Book 데이터를 가져오는 리포지토리 만들기

3. 리포지토리로부터 데이터를 가져와 화면에 출력하기

 

 

폴더(파일)추가

 

모델과 리포지토리 추가

 

 

Book 모델 만들기

 

코드 구조 개선의 첫 번째 단계는 Book 모델을 만드는 것이다.

모델링은 데이터베이스와 관련된 용어로 사물을 코드로 본뜨는 작업을 말한다.

 

지금 프로젝트에선 책의 4가지 특징을 코드로 옮기는데 이는 모델링이라고 한다.

 

Book이라는 객체를 만들어 4가지 필드 값을 다 전달하지 않고

Book 객체 하나를 전달하는 것으로 대체할 수 있다.

 

class Book {
  final String title;
  final String subtitle;
  final String description;
  final String image;
  const Book({
    required this.title,
    required this.subtitle,
    required this.description,
    required this.image,
  });
}

 

Book.dart에 'Book'이라는 class 선언

 

 

리포지토리 만들기

 

서버로부터 데이터를 가져오는 영역의 코드를 리포지토리(repository)라 부른다.

이 프로젝트는 서버와 연결되진 않았지만 더미데이터를 생성하고 가져올 순 있다.

 

import '../models/book.dart';

class BookRepository {
  final List<Book> _dummyBooks = [
    const Book(
        title: '한 권이면 충분해, 프로툴스',
        subtitle: '프로툴스 레코딩, 에디팅, 믹싱, 미디까지 한 권으로 끝내자',
        description:
            '본서는 프로툴스의 설치 방법과 기본적인 사용법부터 레코딩과 믹싱 등의 다양한 기능, 그리고 새롭게 추가된 기능들을 중심으로 내용을 구성했습니다. 음악 작업 시에 사용되지 않는 불필요한 내용들은 과감하게 생략하고 실제 작업에 꼭 필요한 부분만을 다루었습니다. 또한 단순히 프로툴스라는 DAW의 설명에서 벗어나 실제 워크 플로우에 맞게 배울 수 있기 때문에 프로툴스를 처음 사용하는 사람, 더 나아가 엔지니어 지망생, 프로듀서, 싱어송라이터에게 특히 유용한 책이 될 것입니다.',
        image:
            'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnBSOU%2FbtrNb9iQ2X4%2FBtNV0ZUfJeLaUBUKckf3gK%2Fimg.jpg'),
    const Book(
        title: '부동산 공매가 답이다',
        subtitle: '23가지 실제 사례로 마스터하는 공매 투자 비법',
        description:
            '이 책은 문제집처럼 골치 아픈 부동산 이론부터 시시콜콜 강의하는 방식에서 벗어나, 실제로 저자가 경험했던 23가지 생생한 공매 투자 사례들을 엄선한 뒤 기초편, 심화편, 투자편으로 크게 3 STEP으로 묶어 생생하게 배울 수 있도록 구성하였다. 특히 각 사례마다 뒤쪽에 부록 형태로 "여기서 잠깐!"과 "Action Plan" 코너를 추가하여 사이트 접속 방법, 부동산 배경 지식 등 본문에서 미처 다루지 못한 부분을 보충하여 설명하였다.',
        image:
            'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSGHYz%2FbtrM1exYBGl%2Ff8jkIc0ERexQGNW2HL2kAk%2Fimg.jpg'),
    const Book(
        title: '예제부터 배우는 거꾸로 파이썬',
        subtitle: '투자 · 로또 · 리뷰 등 6가지 유용한 주제로 시작하는 데이터 크롤링',
        description:
            '이 책은 독학으로 코딩을 공부한 저자가 만든 여섯 가지 파이썬 실생활 예제를 다룬다. 어렵고 불필요한 문법은 30분 만에 끝내고 ‘로또 당첨 지도’, ‘맛집 검색기’, ‘재무제표 추출’, ‘배달 앱 리뷰 시각화’, ‘이메일 자동화’, ‘스포츠 경기 과거 데이터 수집’과 같이 흥미롭고 유용한 예제를 무작정 따라 만들어 보도록 구성했다. 어려운 문법은 필요 없다! 나에게 필요한 기능만 익혀, 일상의 것들을 자동화하고 데이터를 내가 원하는 모습으로 가공 및 활용해 보자. 『예제부터 배우는 거꾸로 파이썬』과 함께라면 혼자서도 충분히 가능하다.',
        image:
            'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbryLDA%2FbtrIMBW3FAB%2FeFTxvdukNYKr4X8TmtpDik%2Fimg.jpg'),
    const Book(
        title: '한 번에 붙는 SQLD',
        subtitle: '출제 의도 완전 해석ㆍ출제 경향 완전 분석',
        description:
            'SQLD 시험 문제의 원리와 개념에 방점을 두고 집필되었다. SQL을 사용하지 않는 현업 실무자들이나 SQL을 전혀 접하지 못한 사람도 누구나 업무에서 활용이 가능하며, 충분히 이 책을 통해 가볍게 SQLD 시험 준비를 할 수 있도록 구성했다. 다른 어떤 서적을 살펴봐도 이렇게 상세히 원리와 개념을 설명해 주는 책은 없을 것이다.',
        image:
            'https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkewgB%2FbtrFmXb6h4Q%2F8tWAPSroS7IZq6P95Z3780%2Fimg.jpg')
  ];

  List<Book> getBooks() {
    return _dummyBooks;
  }
}

 

Book.dart를 import하여 Book 클래스를 가져온다.

그 후 ListScreen에서 작성했던 책의 데이터들을 _dummyBooks라는 리스트에 넣어준다.

 

getBooks()라는 메서드를 작성해 더미 데이터를 가져올 수 있다.

 

 

리포지토리에서 데이터 가져와 화면에 나타내기

 

레포지토리 불러오기

 

import 'package:flutter/material.dart';
import 'package:flutter_application_1/models/book.dart';
import 'package:flutter_application_1/repositories/book_repository.dart';
import 'package:flutter_application_1/screens/detail_screen.dart';

class ListScreen extends StatelessWidget {
  ListScreen({super.key});
  final List<Book> books = BookRepository().getBooks();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('도서 목록 앱'),
      ),
      body: ListView.builder(
        itemCount: books.length,
        itemBuilder: (BuildContext context, int index) {
          return BookTile(book: books[index]);
        },
      ),
    );
  }
}

 

BookRepository를 불러와 getBooks()를 호출하고 

이를 통해 받아온 데이터(_dummyBooks)를 books에 저장했다.

 

그리고 ListView.Builder()를 활용해 List 형태의 books라는 데이터를

BookTile에 담아 ListView로 만들었다.

 

 

BookTile에서 Book모델 다루기

 

// list_screen.dart

class BookTile extends StatelessWidget {
  final Book book;
  BookTile({
    required this.book
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(book.title),
      leading: Image.network(book.image),
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => DetailScreen(
              book: book,
            ),
          ),
        );
      },
    );
  }
}

BookTile 클래스를 모두 Book 객체로부터 데이터를 뽑아 쓸 수 있도록 수정했다.

그런 다음 DetailScreen으로 book을 전달했다.

 

 

DetailScreen 파일 수정

 

import 'package:flutter/material.dart';
import 'package:flutter_application_1/models/book.dart';

class DetailScreen extends StatelessWidget {
  final Book book;
  const DetailScreen({super.key, required this.book});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book.title),
      ),
      body: Column(
        children: [
          Image.network(book.image),
          const Padding(padding: EdgeInsets.all(3)),
          Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Container(
                  width: MediaQuery.of(context).size.width * 0.8, // overflow 방지
                  padding: const EdgeInsets.all(10),
                  child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Container(
                          child: Text(
                            book.title,
                            style: const TextStyle(
                                fontSize: 23, fontWeight: FontWeight.bold),
                          ),
                        ),
                        Text(
                          book.subtitle,
                          style:
                              const TextStyle(fontSize: 18, color: Colors.grey),
                        )
                      ]),
                ),
                Container(
                  width: MediaQuery.of(context).size.width * 0.15,
                  padding: const EdgeInsets.all(10),
                  child: const Center(
                      child: Icon(
                    Icons.star,
                    color: Colors.red,
                  )),
                ),
              ]),
          const Padding(padding: EdgeInsets.all(3)),
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Column(
                children: [
                  Icon(
                    Icons.call,
                    color: Colors.blue,
                  ),
                  Text('Contact', style: TextStyle(color: Colors.blue)),
                ],
              ),
              Column(
                children: [
                  Icon(
                    Icons.near_me,
                    color: Colors.blue,
                  ),
                  Text('Route', style: TextStyle(color: Colors.blue)),
                ],
              ),
              Column(
                children: [
                  Icon(
                    Icons.save,
                    color: Colors.blue,
                  ),
                  Text('Save', style: TextStyle(color: Colors.blue)),
                ],
              ),
            ],
          ),
          Container(
            padding: const EdgeInsets.all(15),
            child: Text(book.description),
          )
        ],
      ),
    );
  }
}

 

BookTile과 마찬가지로 4가지 필드대신 book 하나만 남긴다.

그 다음 화면을 구성하는 데이터들이 book으로부터 추출되도록 수정한다.

(ex) title -> book.title

 

 

프로젝트에서 한 것들

 

1. UI 작성 (Listview.builder)

2. 화면 이동 & 데이터 전달

3. 데이터 영역과 UI 영역 분리

 

 

깃허브

 

GitHub - sjy1410/Study-Flutter

Contribute to sjy1410/Study-Flutter development by creating an account on GitHub.

github.com

 

'플러터' 카테고리의 다른 글

TodoList 구현  (0) 2023.08.13
타이머 앱  (0) 2023.08.08
플러터로 상태관리  (0) 2023.07.31
플러터 화면 전환 구현  (0) 2023.07.30
플러터 기초쌓기  (0) 2023.07.24
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함