커뮤니티
포인트
쿠폰
내 강의실
국비 신청 내역
증명서
계정
로그아웃
학습 질문
개발 일지
나의 활동
답변 완료
웹페이지로 이동을 안해요
앱개발 종합반 - 플러터 v8
4주차
북마크
진*재
댓글
1
추천
0
조회수
14
조회수
14
답변 완료

오류가 없는데 웹페이지로 이동이 안돼요

왜 이런건가요??



main.dart

import 'package:flutter/material.dart';

import 'package:provider/provider.dart';

import 'package:webview_flutter/webview_flutter.dart';


import 'book.dart';

import 'book_service.dart';


void main() {

 runApp(

  MultiProvider(

   providers: [

    ChangeNotifierProvider(create: (context) => BookService()),

   ],

   child: const MyApp(),

  ),

 );

}


class MyApp extends StatelessWidget {

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


 @override

 Widget build(BuildContext context) {

  return MaterialApp(

   debugShowCheckedModeBanner: false,

   home: HomePage(),

  );

 }

}


class HomePage extends StatefulWidget {

 HomePage({Key? key}) : super(key: key);


 @override

 State<HomePage> createState() => _HomePageState();

}


class _HomePageState extends State<HomePage> {

 var bottomNavIndex = 0;

 @override

 Widget build(BuildContext context) {

  return Scaffold(

   body: [

    SearchPage(),

    LikedBookPage(),

   ].elementAt(bottomNavIndex),

   bottomNavigationBar: BottomNavigationBar(

    selectedItemColor: Colors.black,

    unselectedItemColor: Colors.grey,

    showUnselectedLabels: true,

    selectedFontSize: 12,

    unselectedFontSize: 12,

    iconSize: 28,

    type: BottomNavigationBarType.fixed,

    onTap: (value) {

     setState(() {

      bottomNavIndex = value;

     });

    },

    items: [

     BottomNavigationBarItem(

      icon: Icon(Icons.search),

      label: '검색',

     ),

     BottomNavigationBarItem(

      icon: Icon(Icons.star),

      label: '좋아요',

     ),

    ],

    currentIndex: bottomNavIndex,

   ),

  );

 }

}


class SearchPage extends StatelessWidget {

 SearchPage({super.key});


 @override

 Widget build(BuildContext context) {

  return Consumer<BookService>(

   builder: (context, bookService, child) {

    return Scaffold(

     appBar: AppBar(

      backgroundColor: Colors.white,

      toolbarHeight: 80,

      title: TextField(

       onSubmitted: (value) {

        bookService.search(value);

       },

       cursorColor: Colors.grey,

       decoration: InputDecoration(

        prefixIcon: Icon(Icons.search, color: Colors.grey),

        hintText: "작품, 감독, 배우, 컬렉션, 유저 등",

        border: OutlineInputBorder(

         borderSide: BorderSide(color: Colors.white),

         borderRadius: BorderRadius.all(Radius.circular(10)),

        ),

        focusedBorder: OutlineInputBorder(

         borderSide: BorderSide(color: Colors.grey),

         borderRadius: BorderRadius.all(Radius.circular(10)),

        ),

       ),

      ),

     ),

     body: Padding(

      padding: const EdgeInsets.symmetric(horizontal: 12),

      child: ListView.separated(

       itemCount: bookService.bookList.length,

       separatorBuilder: (context, index) {

        return Divider();

       },

       itemBuilder: (context, index) {

        if (bookService.bookList.isEmpty) return SizedBox();

        Book book = bookService.bookList.elementAt(index);

        return BookTile(book: book);

       },

      ),

     ),

    );

   },

  );

 }

}


class BookTile extends StatelessWidget {

 const BookTile({

  Key? key,

  required this.book,

 }) : super(key: key);

 final Book book;


 @override

 Widget build(BuildContext context) {

  BookService bookService = context.read<BookService>();


  return ListTile(

   onTap: () {

    Navigator.push(

     context,

     MaterialPageRoute(

      builder: (context) => WebViewPage(

       url: book.previewLink.replaceFirst("http", "https"),

      ),

     ),

    );

   },

   leading: Image.network(

    book.thumbnail,

    fit: BoxFit.fitHeight,

   ),

   title: Text(

    book.title,

    style: TextStyle(fontSize: 16),

   ),

   subtitle: Text(

    book.subtitle,

    style: TextStyle(color: Colors.grey),

   ),

   trailing: IconButton(

    onPressed: () {

     bookService.toggleLikeBook(book: book);

    },

    icon: bookService.likedBookList.map((book) => book.id).contains(book.id)

      ? Icon(

        Icons.star,

        color: Colors.amber,

       )

      : Icon(Icons.star_border),

   ),

  );

 }

}


class LikedBookPage extends StatelessWidget {

 const LikedBookPage({super.key});


 @override

 Widget build(BuildContext context) {

  return Consumer<BookService>(

   builder: (context, bookService, child) {

    return Scaffold(

     body: Padding(

      padding: const EdgeInsets.symmetric(horizontal: 12),

      child: ListView.separated(

       itemCount: bookService.likedBookList.length,

       separatorBuilder: (context, index) {

        return Divider();

       },

       itemBuilder: (context, index) {

        if (bookService.likedBookList.isEmpty) return SizedBox();

        Book book = bookService.likedBookList.elementAt(index);

        return BookTile(book: book);

       },

      ),

     ),

    );

   },

  );

 }

}


class WebViewPage extends StatelessWidget {

 WebViewPage({super.key, required this.url});


 String url;


 @override

 Widget build(BuildContext context) {

  return Scaffold(

   appBar: AppBar(

    backgroundColor: Colors.grey,

    title: Text(url),

   ),

   body: WebView(initialUrl: url),

  );

 }

}


book.dart


class Book {

 String id;

 String title;

 String subtitle;

 String thumbnail; // 썸네일 이미지 링크

 String previewLink; // ListTile 을 눌렀을 때 이동하는 링크


 Book({

  required this.id,

  required this.title,

  required this.subtitle,

  required this.thumbnail,

  required this.previewLink,

 }); //이름을 지정해주는 것임

}


book_service.dart



import 'package:dio/dio.dart';

import 'package:flutter/material.dart';


import 'book.dart';


class BookService extends ChangeNotifier {

 List<Book> bookList = []; // 책 목록

 List<Book> likedBookList = [];


 void toggleLikeBook({required Book book}) {

  String bookId = book.id;

  if (likedBookList.map((book) => book.id).contains(bookId)) {

   likedBookList.removeWhere((book) => book.id == bookId);

  } else {

   likedBookList.add(book);

  }

  notifyListeners();

 }


 void search(String q) async {

  bookList.clear(); // 검색 버튼 누를때 이전 데이터들을 지워주기


  if (q.isNotEmpty) {

   Response res = await Dio().get(

    "https://www.googleapis.com/books/v1/volumes?q=$q&startIndex=0&maxResults=40", //$ 뒤는 start부터 40개를 가져오겠다는 뜻

   );

   List items = res.data["items"];


   for (Map<String, dynamic> item in items) {

    Book book = Book(

     id: item['id'],

     title: item['volumeInfo']['title'] ?? "",

     subtitle: item['volumeInfo']['subtitle'] ?? "",

     thumbnail: item['volumeInfo']['imageLinks']?['thumbnail'] ??

       "https://thumbs.dreamstime.com/b/no-image-available-icon-flat-vector-no-image-available-icon-flat-vector-illustration-132482953.jpg",

     previewLink: item['volumeInfo']['previewLink'] ?? "",

    );

    bookList.add(book);

   }

  }

  notifyListeners();

 }

}




취소
 공유
취소
댓글 0
댓글 알림
나의얼굴