Notice
Recent Posts
Recent Comments
Link
250x250
«   2025/01   »
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 31
Tags more
Archives
Today
Total
관리 메뉴

혼자서 앱 만드는 개발자 함께하는 AI 세상

[펫버틀러] 반려 동물 고민 상담 앱 - 개발 7 일차 (강아지 프로필 carousel_slider 페이지적용) 본문

펫버틀러

[펫버틀러] 반려 동물 고민 상담 앱 - 개발 7 일차 (강아지 프로필 carousel_slider 페이지적용)

혼앱사 2022. 12. 26. 21:54
반응형

프로필등록하고 강아지 여러마리 키우는 주인의경우 어떻게 표현하는것이 인식성이 좋을지 고민하다. 선택하게되었다.
일단 아래 링크를 통해 적용된 소스를 가져올수있다.
https://pub.dev/packages/carousel_slider

carousel_slider | Flutter Package

A carousel slider widget, support infinite scroll and custom child widget.

pub.dev



아래 적용된 화면이다.


  • CarouselSlider 를 적용할때 아래 현재위치를 표시하는 부분을 다로 처리 하는것을 알수있다.
  • 별도 위젯으로 만들어서 처리하기로했다. 데이터는 json 리스트를 염두해서 리스트로가져와서 처리하는 부분을 수정했다.
 
 
int _current = 0;
  Widget petList(_controller, List TipsList) {
    return Column(children: [
      Expanded(
          child: CarouselSlider(
        carouselController: _controller,
        options: CarouselOptions(
            height: 400,
            scrollDirection: Axis.horizontal,
            enlargeCenterPage: true,
            viewportFraction: 1.0,
            onPageChanged: (index, reason) {
              setState(() {
                _current = index;
              });
            }),
        items: List.generate(TipsList.length, (i) {
          int index = i;
          return Builder(
            builder: (BuildContext context) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.start,
                children: [
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Expanded(
                        child: Column(
                          children: [
                            const Chip(
                              label: Text(
                                ' 낑낑이 ',
                                style: TextStyle(
                                    color: Colors.white, fontSize: 15.0),
                              ),
                              backgroundColor: Color.fromARGB(255, 90, 91, 91),
                            ),
                            SizedBox(
                              width: MediaQuery.of(context).size.width - 50,
                              child: Card(
                                  shape: RoundedRectangleBorder(
                                    //모서리를 둥글게 하기 위해 사용
                                    borderRadius: BorderRadius.circular(16.0),
                                  ),
                                  elevation: 4.0, //그림자 깊이
                                  child: Column(
                                    children: [
                                      profileImage(index),
                                      itemTextView(Icons.border_color,
                                          "성별:남아 / 생일: 2020.12.25"),
                                      itemTextView(Icons.border_color,
                                          "견종: 푸들 / 외장칩: 231424234"),
                                      SizedBox(height: 10)
                                    ],
                                  )),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ],
              );
            },
          );
        }).toList(),
      )),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [0, 1, 2, 3].asMap().entries.map((entry) {
          return GestureDetector(
            onTap: () => _controller.animateToPage(entry.key),
            child: Container(
              width: 12.0,
              height: 12.0,
              margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
              decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: (Theme.of(context).brightness == Brightness.dark
                          ? Colors.white
                          : Colors.black)
                      .withOpacity(_current == entry.key ? 0.9 : 0.4)),
            ),
          );
        }).toList(),
      ),
    ]);
  }
}
 
  • petList 위젯을 전체 컬럼리스트에 추가해준다.
children: <Widget>[
                        SizedBox(
                            height: 330,
                            child: petList(_controller, jsonPetList)),
                        const SizedBox(height: 20),
                        profileImage(1),
                        const SizedBox(height: 20),
                        const Text('우리집 반려견 사진을 등록해주세요',
                            style: TextStyle(
                                color: Colors.black54,
                                fontWeight: FontWeight.normal,
                                fontFamily: 'S-Core',
                                fontSize: 20.0)),
                        itemTextField(Icons.border_color, pet_name, "이름"),
                        itemTextField(Icons.hub, pet_gender, "성별"),
                        itemTextField(Icons.cake, pet_birthday, "생일"),
                        itemTextField(Icons.share, pet_breed, "견종"),
                        itemTextField(Icons.local_offer, pet_chip, "외장칩"),
                        Container(
                          // A fixed-height child.
                          color: Color.fromARGB(255, 251, 92, 92), // Yellow
                          height: 30.0,
                          alignment: Alignment.center,
                          padding: const EdgeInsets.all(5),
                          child: const Text(' 펫 리스트 ',
                              style: TextStyle(
                                color: Colors.white,
                              )),
                        ),

  • 서버연동전에 슬라이드 적용을 해보았다. 전체소스 아래와 같다. 다음은 서버에 등록하고 리스트로 불러와 슬라이드 화면을 만들어보겠다.

수정된 화면 모습
  • pet_profile.dart 파일 소스
import 'dart:ffi';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:petbutler/pages/memo_page.dart';
import 'package:petbutler/utils/database_helper.dart';
import 'package:petbutler/views/htmlView.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:http/http.dart' as http;

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:provider/provider.dart';

import '../constants/firestore_constants.dart';
import '../providers/auth_provider.dart';

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

  @override
  State<PetProfile> createState() => _HomeState();
}

class _HomeState extends State<PetProfile> with SingleTickerProviderStateMixin {
  late AnimationController controller;
  @override
  void initState() {
    controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 5),
    )..addListener(() {
        setState(() {});
      });
    // controller.repeat(reverse: true);

    users = this.getUsersLIst();

    super.initState();
  }

  Future<List<Auction3>> getUsersLIst() async {
    return await DatabaseHandler().getAllUsers();
  }

  late Future<List<Auction3>> users;
  int _count = 0;
  final descTextStyle = TextStyle(
    color: Colors.black,
    fontWeight: FontWeight.w800,
    fontFamily: 'Roboto',
    letterSpacing: 0.5,
    fontSize: 18,
    height: 2,
  );
  var _visable = false;
  var secondaryTextColor = Colors.white;
  var primaryTextColor = Colors.black;
  late PickedFile _imageFile;
  File? avatarImageFile;
  bool isLoading = false;
  TextEditingController pet_name = TextEditingController();
  TextEditingController pet_birthday = TextEditingController();
  TextEditingController pet_gender = TextEditingController();
  TextEditingController pet_breed = TextEditingController();
  TextEditingController pet_chip = TextEditingController();
  late AuthProvider authProvider;

  List jsonPetList = [
    ['낑낑이1', 'images/pet/intro1.png'],
    ['낑낑이2', 'images/pet/intro2.png'],
    ['낑낑이3', 'images/pet/intro3.png'],
    ['낑낑이4', 'images/pet/intro4.png'],
  ];

  var fileData = "";
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  // 카메라/갤러리에서 사진 가져올 때 사용함 (image_picker)
  final ImagePicker _picker = ImagePicker();
  final CarouselController _controller = CarouselController();
  @override
  Widget build(BuildContext context) {
    authProvider = Provider.of<AuthProvider>(context);

    Future<http.Response> sendTokenMessage(String id) {
      return http.post(
        Uri.parse(
            'https://iukj.cafe24.com/SETDATA/petbutler/MENU_MGT_S005_S100'),
        headers: <String, String>{
          'Content-Type': 'application/json; charset=UTF-8',
        },
        body: jsonEncode(<String, String>{
          "mb1": id,
          "mb2": pet_name.text,
          "mb3": pet_birthday.text,
          "mb4": pet_gender.text,
          "mb5": pet_breed.text,
          "mb6": pet_chip.text,
          "mb7": fileData,
        }),
      );
    }

    return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.transparent,
          bottomOpacity: 0.0,
          elevation: 0.0,
          //title: const Text('경매수익률분석'),
          leading: IconButton(
            icon: const Icon(Icons.add_a_photo),
            tooltip: '사진등록',
            onPressed: () {},
          ),
          actions: <Widget>[
            Center(
              child: Padding(
                padding: const EdgeInsets.fromLTRB(0, 0, 8, 0),
                child: InkWell(
                  onTap: () {},
                  child: TextButton(
                      style: ButtonStyle(
                        backgroundColor:
                            MaterialStateProperty.resolveWith<Color?>(
                                (Set<MaterialState> states) {
                          return Color.fromARGB(255, 33, 31,
                              31); // Defer to the widget's default.
                        }),
                      ),
                      onPressed: () async {
                        if (_formKey.currentState!.validate()) {
                          if (authProvider.getUserFirebaseId()?.isNotEmpty ==
                              true) {
                            var currentUserId =
                                authProvider.getUserFirebaseId()!;
                            sendTokenMessage(currentUserId);
                          }
                        }
                      },
                      child: const Text(
                        '등록',
                        style: TextStyle(
                          color: Color.fromARGB(255, 248, 246, 246),
                        ),
                      )),
                ),
              ),
            ),
          ],
        ),
        // We're using a Builder here so we have a context that is below the Scaffold
        // to allow calling Scaffold.of(context) so we can show a snackbar.

        body: Builder(builder: (BuildContext context) {
          const labelTextStyle =
              TextStyle(fontSize: 10, fontStyle: FontStyle.italic);

          DatabaseHandler dbHandler = DatabaseHandler();

          return DefaultTextStyle(
            style: Theme.of(context).textTheme.bodyText2!,
            child: LayoutBuilder(
              builder:
                  (BuildContext context, BoxConstraints viewportConstraints) {
                return SingleChildScrollView(
                    child: ConstrainedBox(
                  constraints: BoxConstraints(
                    minHeight: viewportConstraints.minHeight,
                  ),
                  child: Form(
                    key: _formKey,
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      mainAxisAlignment: MainAxisAlignment.spaceAround,
                      children: <Widget>[
                        SizedBox(
                            height: 330,
                            child: petList(_controller, jsonPetList)),
                        const SizedBox(height: 20),
                        profileImage(1),
                        const SizedBox(height: 20),
                        const Text('우리집 반려견 사진을 등록해주세요',
                            style: TextStyle(
                                color: Colors.black54,
                                fontWeight: FontWeight.normal,
                                fontFamily: 'S-Core',
                                fontSize: 20.0)),
                        itemTextField(Icons.border_color, pet_name, "이름"),
                        itemTextField(Icons.hub, pet_gender, "성별"),
                        itemTextField(Icons.cake, pet_birthday, "생일"),
                        itemTextField(Icons.share, pet_breed, "견종"),
                        itemTextField(Icons.local_offer, pet_chip, "외장칩"),
                        Container(
                          // A fixed-height child.
                          color: Color.fromARGB(255, 251, 92, 92), // Yellow
                          height: 30.0,
                          alignment: Alignment.center,
                          padding: const EdgeInsets.all(5),
                          child: const Text(' 펫 리스트 ',
                              style: TextStyle(
                                color: Colors.white,
                              )),
                        ),
                        SizedBox(
                          height: 400,
                          child: FutureBuilder(
                              future: users,
                              builder: (context, snapshot) {
                                if (snapshot.hasData) {
                                  var usersList =
                                      snapshot.data as List<Auction3>;
                                  return ListView.builder(
                                      itemCount: usersList.length,
                                      itemBuilder:
                                          (BuildContext context, int index) {
                                        Auction3 user = usersList[index];
                                        return ListTile(
                                          title: TextButton(
                                              onPressed: () {},
                                              child: Align(
                                                  alignment:
                                                      Alignment.centerLeft,
                                                  child: Text(
                                                    textAlign: TextAlign.left,
                                                    user.sbjt.toString() +
                                                        "    (" +
                                                        user.aucM1.toString() +
                                                        ")",
                                                    style: TextStyle(
                                                        color: Colors.black54),
                                                  ))),
                                          trailing: IconButton(
                                            onPressed: () {
                                              _delete(context, dbHandler, user);
                                            },
                                            icon: const Icon(Icons.delete),
                                          ),
                                        );
                                      });
                                } else {
                                  return CircularProgressIndicator(
                                    value: controller.lowerBound,
                                    semanticsLabel:
                                        'Circular progress indicator',
                                  );
                                }
                              }),
                        ),
                      ],
                    ),
                  ),
                ));
              },
            ),
          );
        }));
  }

  bool _isShown = true;

  void _delete(BuildContext context, dbHandler, user) {
    showCupertinoDialog(
        context: context,
        builder: (BuildContext ctx) {
          return CupertinoAlertDialog(
            title: const Text('삭제하기'),
            content: const Text('해당물건을 삭제하시겠습니까?'),
            actions: [
              // The "Yes" button
              CupertinoDialogAction(
                onPressed: () {
                  dbHandler.deleteUser(user.aucNo);

                  Navigator.of(context).pop();
                  initState();
                  setState(() {});
                },
                child: const Text('삭제'),
                isDefaultAction: true,
                isDestructiveAction: true,
              ),
              // The "No" button
              CupertinoDialogAction(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: const Text('취소'),
                isDefaultAction: false,
                isDestructiveAction: false,
              )
            ],
          );
        });
  }

  Widget profileImage(index) {
    ImageProvider aa;
    return Center(
      child: Stack(
        children: <Widget>[
          avatarImageFile == null
              ? CircleAvatar(
                  radius: 80,
                  backgroundImage:
                      AssetImage('images/pet/intro/intro$index.png'))
              : CircleAvatar(
                  radius: 80,
                  backgroundImage: FileImage(avatarImageFile!),
                ),
          Positioned(
              bottom: 20,
              right: 20,
              child: InkWell(
                onTap: () async {
                  final pickedFile =
                      await _picker.getImage(source: ImageSource.camera);

                  File? image;
                  if (pickedFile != null) {
                    image = File(pickedFile.path);
                  }
                  if (image != null) {
                    setState(() {
                      avatarImageFile = image;
                      isLoading = true;
                    });
                    // uploadFile();
                  }

                  /*showModalBottomSheet(
                      context: context, builder: ((builder) => bottomSheet()));
                      */
                },
                child: Icon(
                  Icons.photo_camera,
                  color: secondaryTextColor,
                  size: 40,
                ),
              ))
        ],
      ),
    );
  }

  Widget itemTextField(IconData icon, TextEditingController ctr, String name) {
    return Container(
        // A fixed-height child.
        color: Colors.white, // Yellow

        // height: 50.0,
        alignment: Alignment.center,
        padding: const EdgeInsets.fromLTRB(50, 0, 50, 0),
        child: TextFormField(
          controller: ctr,
          validator: (val) {
            // ignore: curly_braces_in_flow_control_structures
            if (name == '이름') if (val!.isEmpty) {
              return '$name 필수사항입니다.';
            } else {
              return null;
            }

            return null;
          },
          decoration: InputDecoration(
              prefixIcon: Icon(
                icon,
                color: primaryTextColor,
              ),
              //  labelText: '이름',
              hintText: name),
        ));
  }

  Widget itemTextView(IconData icon, String name) {
    return Container(
        // A fixed-height child.
        color: Colors.white, // Yellow

        // height: 50.0,
        alignment: Alignment.center,
        padding: const EdgeInsets.fromLTRB(0, 3, 0, 0),
        child: Text(name, style: TextStyle(fontSize: 15.0)));
  }

  Widget bottomSheet() {
    return Container(
        height: 100,
        width: MediaQuery.of(context).size.width,
        margin: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
        child: Column(
          children: <Widget>[
            Text(
              'Choose Profile photo',
              style: TextStyle(
                fontSize: 20,
              ),
            ),
            SizedBox(
              height: 20,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                IconButton(
                  icon: Icon(
                    Icons.camera,
                    size: 50,
                  ),
                  onPressed: () {
                    takePhoto(ImageSource.camera);
                  },
                ),
                IconButton(
                    icon: Icon(
                      Icons.photo_library,
                      size: 50,
                    ),
                    onPressed: () {
                      takePhoto(ImageSource.gallery);
                    })
              ],
            )
          ],
        ));
  }

  takePhoto(ImageSource source) async {
    final pickedFile = await _picker.getImage(source: source);
    setState(() {});
  }

  double nullChangeValue(String editText) {
    try {
      return double.parse(editText);
    } on Exception {
      return 0.0;
    }
  }

  String doubleToString(double d) {
    return chage000(d);
  }

  String doubleNotIntToString(double d) {
    // d=Math.round(d*100/11)/100;
    return (d.toString());
  }

  double nullChangeDoubleValue(String editText) {
    return double.parse(editText);
  }

  String chage000(double str) {
    // Thousand-separator
    var f = NumberFormat('###,###,###,###.##');
    return f.format(str);
  }

  int _current = 0;
  Widget petList(_controller, List TipsList) {
    return Column(children: [
      Expanded(
          child: CarouselSlider(
        carouselController: _controller,
        options: CarouselOptions(
            height: 400,
            scrollDirection: Axis.horizontal,
            enlargeCenterPage: true,
            viewportFraction: 1.0,
            onPageChanged: (index, reason) {
              setState(() {
                _current = index;
              });
            }),
        items: List.generate(TipsList.length, (i) {
          int index = i;
          return Builder(
            builder: (BuildContext context) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.start,
                children: [
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Expanded(
                        child: Column(
                          children: [
                            const Chip(
                              label: Text(
                                ' 낑낑이 ',
                                style: TextStyle(
                                    color: Colors.white, fontSize: 15.0),
                              ),
                              backgroundColor: Color.fromARGB(255, 90, 91, 91),
                            ),
                            SizedBox(
                              width: MediaQuery.of(context).size.width - 50,
                              child: Card(
                                  shape: RoundedRectangleBorder(
                                    //모서리를 둥글게 하기 위해 사용
                                    borderRadius: BorderRadius.circular(16.0),
                                  ),
                                  elevation: 4.0, //그림자 깊이
                                  child: Column(
                                    children: [
                                      profileImage(index),
                                      itemTextView(Icons.border_color,
                                          "성별:남아 / 생일: 2020.12.25"),
                                      itemTextView(Icons.border_color,
                                          "견종: 푸들 / 외장칩: 231424234"),
                                      SizedBox(height: 10)
                                    ],
                                  )),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ],
              );
            },
          );
        }).toList(),
      )),
      Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [0, 1, 2, 3].asMap().entries.map((entry) {
          return GestureDetector(
            onTap: () => _controller.animateToPage(entry.key),
            child: Container(
              width: 12.0,
              height: 12.0,
              margin: EdgeInsets.symmetric(vertical: 8.0, horizontal: 4.0),
              decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: (Theme.of(context).brightness == Brightness.dark
                          ? Colors.white
                          : Colors.black)
                      .withOpacity(_current == entry.key ? 0.9 : 0.4)),
            ),
          );
        }).toList(),
      ),
    ]);
  }
}

Widget perNumber(String str, labelTextStyle, TextEditingController value) =>
    Row(
      children: <Widget>[
        Container(
          margin: EdgeInsets.all(5),
          //padding: EdgeInsets.fromLTRB(10, 20, 30, 40),
          child: SizedBox(height: 30, width: 100, child: Text(str)),
        ),

        const SizedBox(
          height: 20,
          width: 10,
          //child: Container(color: Colors.blue),
        ), //container

        SizedBox(
          height: 30,
          width: 150,
          child: TextField(
            //obscureText: true,
            controller: value,
            keyboardType: TextInputType.number,
            //  inputFormatters: [FilteringTextInputFormatter.],
            decoration: InputDecoration(
              border: const OutlineInputBorder(),
              labelText: '금액',
              labelStyle: labelTextStyle,
            ),
          ),
        ),
        //container
      ], //widget
    );
728x90
반응형
Comments