name27 / flutter

0 stars 0 forks source link

도전과제: 네이버 메일 앱 #91

Open name27 opened 1 year ago

name27 commented 1 year ago

image image image image image image image image image

main.dart

import 'package:flutter/material.dart';

import 'pages/main_page.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MainPage(),
    );
  }
}

main_page.dart

import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:naver_clone_app/pages/search_page.dart';

import 'mail_page.dart';

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

  @override
  State<MainPage> createState() => _MainPageState();
}

var listViewController = ScrollController();

class _MainPageState extends State<MainPage> {
  ValueNotifier<bool> isCur = ValueNotifier<bool>(true);

  var res;
  Future<Map<String, dynamic>> getData() async {
    var dio = Dio();
    res = await dio.get(
        'https://sfacassignmentchallenge-default-rtdb.europe-west1.firebasedatabase.app/.json');
    if (res.statusCode == 200) {
      return res.data;
    }
    return {};
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: GestureDetector(
            onTap: () {
              listViewController.animateTo(0,
                  duration: Duration(milliseconds: 300), curve: Curves.easeIn);
            },
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('프로모션'),
                Padding(
                  padding: const EdgeInsets.all(3.0),
                  child: Icon(
                    Icons.circle,
                    color: Colors.greenAccent[400],
                    size: 9,
                  ),
                ),
                Text(
                  '34',
                  style: TextStyle(color: Colors.greenAccent[400]),
                )
              ],
            ),
          ),
          centerTitle: true,
          foregroundColor: Colors.black,
          backgroundColor: Colors.transparent,
          elevation: 0,
          actions: [
            IconButton(
                onPressed: () {
                  Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) =>
                              SearchPage(data: res.data["emails"])));
                },
                icon: Icon(
                  Icons.search_outlined,
                  size: 30,
                  color: Colors.grey,
                )),
            IconButton(
                onPressed: () {
                  isCur.value = !isCur.value;
                },
                icon: Icon(
                  Icons.access_time,
                  size: 30,
                  color: Colors.grey,
                )),
          ],
        ),
        drawer: Drawer(
          child: ListView(
            padding: EdgeInsets.zero,
            children: [
              UserAccountsDrawerHeader(
                currentAccountPicture: CircleAvatar(
                  backgroundColor: Colors.white,
                  child: Icon(
                    Icons.person,
                    size: 50,
                    color: Colors.grey,
                  ),
                ),
                accountName: Text('Test'),
                accountEmail: Text('test@naver.com'),
                onDetailsPressed: () {
                  print('clicked');
                },
                decoration: BoxDecoration(
                  color: Colors.grey,
                ),
              ),
              ListTile(
                leading: Icon(Icons.mail),
                title: Text('전체 메일'),
              ),
              ListTile(
                leading: Icon(Icons.email_outlined),
                title: Text('받은 메일'),
              ),
              ListTile(
                leading: Icon(Icons.send),
                title: Text('보낸 메일함'),
              ),
            ],
          ),
        ),
        body: Center(
          child: FutureBuilder(
            future: getData(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const CupertinoActivityIndicator();
              }
              if (!snapshot.hasData) return const Text("데이터가 없습니다");
              Map<String, dynamic> data = snapshot.data as Map<String, dynamic>;
              List<dynamic> emails = data["emails"];
              return Container(
                  decoration: BoxDecoration(color: Colors.grey.shade300),
                  child: MailPage(
                    emails: emails,
                    listViewController: listViewController,
                    isCur: isCur,
                    isWidget: false,
                  ));
            },
          ),
        ),
        floatingActionButton: MailPage(
          isWidget: true,
        ));
  }
}

mail_page.dart

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:naver_clone_app/pages/mailDetail_page.dart';
import 'package:naver_clone_app/widget/deleteMail.dart';
import 'package:naver_clone_app/widget/mailCard.dart';
import 'package:naver_clone_app/model/maildata.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';

import 'main_page.dart';

class MailPage extends StatefulWidget {
  const MailPage(
      {super.key,
      this.emails,
      this.listViewController,
      this.isCur,
      required this.isWidget});
  final dynamic emails;
  final dynamic listViewController;
  final dynamic isCur;
  final bool isWidget;

  @override
  State<MailPage> createState() => _MailPageState();
}

var _refreshController = RefreshController();
List<Map<String, dynamic>> deleteMail = [];
var i = 0;

class _MailPageState extends State<MailPage> {
  void _onRefresh() async {
    setState(() {});
    await Future.delayed(Duration(milliseconds: 500));
    _refreshController.refreshCompleted();
  }

  @override
  Widget build(BuildContext context) {
    if (!widget.isWidget) {
      return ValueListenableBuilder(
        valueListenable: widget.isCur,
        builder: (context, value, child) {
          var emails = widget.emails;
          if (!widget.isCur.value) {
            emails = List.from(widget.emails.reversed);
          }
          return SmartRefresher(
            enablePullUp: false,
            header: WaterDropHeader(
                waterDropColor: Colors.transparent,
                idleIcon: Icon(
                  Icons.refresh_outlined,
                  size: 35,
                )),
            controller: _refreshController,
            onRefresh: _onRefresh,
            child: ListView.builder(
                controller: listViewController,
                itemBuilder: (context, index) {
                  Mail emaildata = Mail.fromMap(emails[index]);
                  var sendDateTime =
                      DateTime.parse(emaildata.sendDate.replaceAll('.', '-'));
                  if (sendDateTime.year == DateTime.now().year) {
                    emaildata.sendDate =
                        DateFormat('MM.dd').format(sendDateTime);
                    if (sendDateTime.month == DateTime.now().month) {
                      var day = DateTime.now().day - sendDateTime.day;
                      switch (day) {
                        case 0:
                          emaildata.sendDate = "오늘";
                          break;
                        case 1:
                          emaildata.sendDate = "어제";
                          break;
                      }
                    }
                  } else if (sendDateTime.year != DateTime.now().year) {
                    emaildata.sendDate =
                        DateFormat('yyyy.MM.dd').format(sendDateTime);
                  }
                  return Dismissible(
                      direction: DismissDirection.endToStart,
                      background: Container(
                        color: Colors.red,
                        child: Center(
                            child: Icon(
                          Icons.delete_outline,
                          size: 40,
                        )),
                      ),
                      key: UniqueKey(),
                      onDismissed: (direction) {
                        if (emails[index] != null) {
                          deleteMail.add({});
                          deleteMail[i].addAll(emails[index]);
                          i++;
                        }
                        setState(() {
                          emails.removeAt(index);
                        });
                      },
                      child: GestureDetector(
                        onTap: (){
                          Navigator.push(context, MaterialPageRoute(builder: (context) => MailDetailPage(data: emails[index]),));
                        },
                        child: MailCard(
                          emaildata: emaildata,
                        ),
                      ));
                },
                itemCount: emails.length),
          );
        },
      );
    } else {
      return DeleteMail(deleteMail: deleteMail);
    }
  }
}

mailDetail_page.dart

import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';

class MailDetailPage extends StatelessWidget {
  const MailDetailPage({super.key, required this.data});
  final Map<String, dynamic> data;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        foregroundColor: Colors.black,
        elevation: 0.5,
        actions: [
          IconButton(
              onPressed: () {},
              icon: Icon(
                Icons.mail_outline_outlined,
                size: 30,
              )),
          IconButton(
              onPressed: () {},
              icon: Icon(
                Icons.delete_forever,
                size: 30,
              )),
          IconButton(
              onPressed: () {},
              icon: Icon(
                Icons.more_vert,
                size: 30,
              ))
        ],
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.only(left:8.0, top: 8, right: 8),
            child: Row(
              children: [
                Text('보낸 사람'),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal:3.0),
                  child: Icon(
                    Icons.arrow_drop_down_circle,
                    color: Colors.grey,
                    size: 15,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal:5.0),
                  child: Text(data["from"]),
                )
              ],
            ),
          ),
          Divider(
            thickness: 1,
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal:8.0),
            child: Text(
              data["title"],
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
            ),
          ),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal:8.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(data["sendDate"]),
                Icon(
                  Icons.star,
                  color: Colors.grey,
                )
              ],
            ),
          ),
          Divider(
            thickness: 1,
          ),
          Padding(
            padding: const EdgeInsets.all(12.0),
            child: Container(
              width: 400,
              child: RichText(
                overflow: TextOverflow.clip,
                strutStyle: StrutStyle(fontSize: 16.0),
                text: TextSpan(
                    text: data["detail"],
                    style: TextStyle(
                        fontSize: 15,
                        color: Colors.black,
                        fontWeight: FontWeight.w500)),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

search_page.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:naver_clone_app/model/maildata.dart';
import 'package:naver_clone_app/pages/mailDetail_page.dart';
import 'package:naver_clone_app/widget/mailCard.dart';

class SearchPage extends StatefulWidget {
  const SearchPage({super.key, required this.data});
  final List<dynamic> data;

  @override
  State<SearchPage> createState() => _SearchPageState();
}

var _inputController = TextEditingController();
List<String> searchList = [];
bool _isSearch = false;
bool _isCorrect = false;
var _searchIndex;

class _SearchPageState extends State<SearchPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        foregroundColor: Colors.black,
        elevation: 0,
        title: TextField(
          onSubmitted: (value) {
            _isSearch = !_isSearch;
            searchList.insert(0, value);
            for (int i = 0; i < widget.data.length; i++) {
              if (widget.data[i]["from"] == value) {
                _isCorrect = true;
                _searchIndex = i;
              }
            }
            setState(() {});

            print(searchList);
          },
          controller: _inputController,
          decoration: InputDecoration(
            hintText: '메일 검색',
            enabledBorder: OutlineInputBorder(
              borderSide: BorderSide(
                color: Colors.transparent,
                width: 0.3,
              ),
            ),
          ),
        ),
        actions: [
          IconButton(
              onPressed: () {
                _inputController.text = "";
                _isCorrect = false;
                setState(() {});
              },
              icon: Icon(Icons.close))
        ],
      ),
      body: _isSearch && _isCorrect
          ? InkWell(
              onTap: () {
                _isCorrect = false;
                Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) =>
                          MailDetailPage(data: widget.data[_searchIndex]),
                    ));
              },
              child: MailCard(
                emaildata: Mail.fromMap(widget.data[_searchIndex]),
              ),
            )
          : ListView.separated(
              itemCount: searchList.length,
              itemBuilder: (context, index) {
                return Padding(
                  padding: const EdgeInsets.only(
                      left: 12.0, right: 12, bottom: 5, top: 5),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Row(
                            children: [
                              TextButton(
                                onPressed: () {
                                  _inputController.text = searchList[index];
                                },
                                child: Text(
                                  searchList[index],
                                  style: TextStyle(color: Colors.black),
                                ),
                              ),
                              TextButton(
                                onPressed: () {
                                  for (int i = 0; i < widget.data.length; i++) {
                                    if (widget.data[i]["from"] ==
                                        searchList[index]) {
                                      Navigator.push(
                                          context,
                                          MaterialPageRoute(
                                            builder: (context) =>
                                                MailDetailPage(
                                              data: widget.data[i],
                                            ),
                                          ));
                                    }
                                  }
                                },
                                child: Text(
                                  ' (전체) ',
                                  style: TextStyle(color: Colors.grey),
                                ),
                              ),
                            ],
                          ),
                          IconButton(
                              onPressed: () {
                                searchList.removeAt(index);
                                setState(() {});
                              },
                              icon: Icon(
                                Icons.close,
                                color: Colors.grey,
                              ))
                        ],
                      ),
                    ],
                  ),
                );
              },
              separatorBuilder: (context, index) => Divider(
                thickness: 2,
              ),
            ),
    );
  }
}

deleteMail.dart

import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:intl/intl.dart';

import '../model/maildata.dart';
import 'mailCard.dart';

class DeleteMail extends StatelessWidget {
  const DeleteMail({super.key, required this.deleteMail});
  final List deleteMail;

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: () {
        showModalBottomSheet<void>(
          context: context,
          builder: (BuildContext context) {
            return ListView.builder(
                itemBuilder: (context, index) {
                  Mail emaildata = Mail.fromMap(deleteMail[index]);
                  var sendDateTime =
                      DateTime.parse(emaildata.sendDate.replaceAll('.', '-'));
                  if (sendDateTime.year == DateTime.now().year) {
                    emaildata.sendDate =
                        DateFormat('MM.dd').format(sendDateTime);
                    if (sendDateTime.month == DateTime.now().month) {
                      var day = DateTime.now().day - sendDateTime.day;
                      switch (day) {
                        case 0:
                          emaildata.sendDate = "오늘";
                          break;
                        case 1:
                          emaildata.sendDate = "어제";
                          break;
                      }
                    }
                  } else if (sendDateTime.year != DateTime.now().year) {
                    emaildata.sendDate =
                        DateFormat('yyyy.MM.dd').format(sendDateTime);
                  }
                  return MailCard(
                    emaildata: emaildata,
                  );
                },
                itemCount: deleteMail.length);
            ;
          },
        );
      },
      backgroundColor: Colors.greenAccent[400],
      child: Icon(
        Icons.delete_outline_outlined,
        size: 35,
      ),
    );
  }
}

mailCard.dart

import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/widgets.dart';
import 'package:naver_clone_app/model/maildata.dart';

class MailCard extends StatelessWidget {
  const MailCard({super.key, required this.emaildata});
  final Mail emaildata;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 3.0),
      child: AspectRatio(
        aspectRatio: 7 / 2,
        child: Card(
          child: Padding(
            padding: const EdgeInsets.all(3.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Column(
                  children: [
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Row(
                          children: [
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Icon(
                                Icons.circle,
                                size: 8,
                                color: Colors.greenAccent[400],
                              ),
                            ),
                            Text(emaildata.title,
                                style: TextStyle(
                                    fontWeight: FontWeight.w600, fontSize: 16)),
                          ],
                        ),
                        Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Text(emaildata.sendDate),
                            Icon(
                              Icons.star,
                              color: Colors.grey.shade300,
                            )
                          ],
                        )
                      ],
                    ),
                    Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8.0),
                      child: Row(
                        children: [
                          Padding(
                            padding:
                                const EdgeInsets.symmetric(horizontal: 4.0),
                            child: Container(
                                padding: EdgeInsets.symmetric(horizontal: 5.5),
                                decoration: BoxDecoration(
                                    color: Colors.grey,
                                    borderRadius: BorderRadius.circular(13)),
                                child: Text(
                                  'To',
                                  style: TextStyle(color: Colors.white),
                                )),
                          ),
                          Container(
                            width: 300,
                            child: RichText(
                              overflow: TextOverflow.clip,
                              maxLines: 1,
                              strutStyle: StrutStyle(fontSize: 16.0),
                              text: TextSpan(
                                  text: emaildata.from,
                                  style: TextStyle(
                                      fontSize: 15,
                                      color: Colors.black,
                                      fontWeight: FontWeight.w500)),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
                  child: Container(
                    width: 350,
                    child: RichText(
                      overflow: TextOverflow.clip,
                      maxLines: 2,
                      strutStyle: StrutStyle(fontSize: 16.0),
                      text: TextSpan(
                          text: emaildata.detail,
                          style: TextStyle(fontSize: 15, color: Colors.black)),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Widget _buildMailCard() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 3.0),
      child: AspectRatio(
        aspectRatio: 7 / 2,
        child: Card(
          child: Padding(
            padding: const EdgeInsets.all(3.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Column(
                  children: [
                    Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Row(
                          children: [
                            Padding(
                              padding: const EdgeInsets.all(8.0),
                              child: Icon(
                                Icons.circle,
                                size: 8,
                                color: Colors.greenAccent[400],
                              ),
                            ),
                            Text(emaildata.title,
                                style: TextStyle(
                                    fontWeight: FontWeight.w600, fontSize: 16)),
                          ],
                        ),
                        Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Text(emaildata.sendDate),
                            Icon(
                              Icons.star,
                              color: Colors.grey.shade300,
                            )
                          ],
                        )
                      ],
                    ),
                    Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8.0),
                      child: Row(
                        children: [
                          Padding(
                            padding:
                                const EdgeInsets.symmetric(horizontal: 4.0),
                            child: Container(
                                padding: EdgeInsets.symmetric(horizontal: 5.5),
                                decoration: BoxDecoration(
                                    color: Colors.grey,
                                    borderRadius: BorderRadius.circular(13)),
                                child: Text(
                                  'To',
                                  style: TextStyle(color: Colors.white),
                                )),
                          ),
                          Container(
                            width: 300,
                            child: RichText(
                              overflow: TextOverflow.clip,
                              maxLines: 1,
                              strutStyle: StrutStyle(fontSize: 16.0),
                              text: TextSpan(
                                  text: emaildata.from,
                                  style: TextStyle(
                                      fontSize: 15,
                                      color: Colors.black,
                                      fontWeight: FontWeight.w500)),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
                  child: Container(
                    width: 350,
                    child: RichText(
                      overflow: TextOverflow.clip,
                      maxLines: 2,
                      strutStyle: StrutStyle(fontSize: 16.0),
                      text: TextSpan(
                          text: emaildata.detail,
                          style: TextStyle(fontSize: 15, color: Colors.black)),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}