반응형

Pocket A

 
 
안녕하세요. 2024년 용띠해가 되었네요.
많은 일들이 잘 되길 빕니다.~
 

 
스터디는 원래 실전이 가장 좋은 방법이라고 하죠?, 저도 Flutter 스터디를 위해서 2가지 프로젝트를 예전부터 진행했었습니다.
하나는 게임을 한번 만들어보는 것이고, 하나는 집에서 가족들과 사용할 수 있는 애플리케이션을 만들어보는 거였는데요.
 
그중 하나인 Pocket A 라는 애플리케이션입니다.
https://play.google.com/store/apps/details?id=com.rjlife.pocketa

 

포켓 - Google Play 앱

할일을 기록하고 실천해보세요. 나의 루틴을 관리하고 , Chat GPT, Dall-E 로 멋진 라이프 스타일을 만들어 보세요.

play.google.com

 
 
 
 
이 애플리케이션은 "아내가 집에서 입는 옷을 어떻게 하면 쉽게 관리하고 매칭해서 입을까?"라는 질문에서 시작했습니다.

처음 아이디어가 나왔을때는, "사진찍어서 바로 올리고", "계절별 매칭도 해주고", "집에 있는 옷장이 한눈에 보이고" 듣기에 그럴듯해보이는 것 같았습니다. 프로젝트를 구상하기 시작한지 몇일 지나자 초기의 옷 정리를 위한 앱의 정체성과 니즈에 대해서 복잡한 고민을 하기 시작했습니다. "사진을 찍어봤는데 별로 안이쁜데?", "옷 매칭을 하려면 옷을 잘 찍어놔야하고 또 배경도 없애고, 모델도 필요한거 아닌가?", "옷을 종류별 계절별로 나눠보니 몇벌 안나오네?" 등등등... 뭔가 상상과 현실이 안맞는 그런 상황에서의 고민이었죠.

 

그러다 그 시점에 Chat GPT가 뜨면서 Chat GPT 앱이 되어버렸습니다. (^^;;  역시 제대로 된 기획이 없으니 산으로 가는.. 쿨럭)

 

그래서 이런 애플리케이션이 되었습니다. 
 

 
Dali-E 같은 경우는 원래 영어로 입력해야 이미지를 만들어주는데, 좀 불편해서 한글을 영어로 자동 번역해서 처리되도록 해봤습니다.
 
그리고 2024년을 기념하여, 새해 계획들을 많이 세우는데 사실 실천이 어렵죠... 
와이프의 생활패턴을 보니 계획 실천하는데 매일 Todo를 적고 하나씩 해나가는 것이 좀 인상적이었습니다.
(본인은 잊어먹지 않고 하려고 적는다고 한다고 하는데, 나이가 들고 보니 무척 좋은 습관인 것 같네요.)
 
 

한동안 따로 개선하지 않다가 드디어 할 일?? 이 생겼습니다. ^^
아무튼 저는 개발자이니 이것을 개발자스럽게 해결해 보고자 앱에 TODO 기능을 넣었습니다.
 
짜잔.
 

 
 
 

 
날짜 별로 할 일을 추가할 수 있습니다.
운동이나 약 먹기 같은 매일 하고자 하는 루틴의 경우에는 매일 할 일을 체크 해두면 매일 해야 할 일로 나옵니다.
그리고 할 일을 일정시간 동안 투자 해야 하는 경우라면 간혹 타이머가 필요한데, 그래서 타이머를 이렇게 만들어 놨습니다.
 
 
이렇게 해서 간단한 할 일 기능이 추가되었답니다.
+ 사용하다 보니, 이전할 일들 중에서 아직 완료하지 못한 것들을 어떻게 표현하면 좋을까? 고민하고 있는데 했는데 무작정 이전에 완료 안된 일들을 남겨 놓는 것도 아닌 것 같고, 이 부분은 좀 아이디어를 찾아봐야 할 것 같습니다.
 
 
 
 
#개발 이야기
 
Framework :  Flutter 3.16.0
주요 pub:

get: ^4.6.5
shared_preferences: ^2.0.15
intl: ^0.18.1
http: ^0.13.5
flutter_native_splash: ^2.2.19
flex_color_scheme: ^7.3.1
#animated_text_kit: 4.2.2
animated_text_kit:
git:
url: https://github.com/karzia/Animated-Text-Kit.git
package_info_plus: ^3.0.2
json_store: ^2.0.1
permission_handler: ^11.1.0
path_provider: ^2.0.14
upgrader: ^6.2.0
photo_view: ^0.14.0
flutter_staggered_grid_view: ^0.7.0
share_plus: ^7.2.1
equatable: ^2.0.5
flutter_bloc: ^8.1.2
image_picker: ^0.8.6+1
carousel_slider: ^4.2.1
url_launcher: ^6.1.10
# for objectbox (Database)
objectbox: ^2.3.1
objectbox_flutter_libs: any

easy_date_timeline: ^1.0.2
gap: ^3.0.1


flutter_speed_dial: ^3.0.5

 
 
animated_text_kit : intro(splash) 쪽이 너무 밋밋하여 뭔가 포인트를 주기 위해서 이용했습니다.
 - 생각보다 이쁘게 나와서 chat에서 waiting시에... 에 대한 animation에도 사용했는데요. 높이 조절이 잘 안 되고 너무 튀어서 customizing을 따로 했습니다.
 
objectbox: todo를 추가하다 보니 database가 필요해져서 nosql로 간단하게 사용할 것을 찾다가 이를 이용했습니다.
 - 설명에는 경량이고 추후 cloud sync 연동도 가능하다고 되어있어서 사용해 보게 되었습니다.
 
package_info_plus: package version 정보를 보여주기 위해서 사용
flex_color_scheme: 유튜브에서 theme을 쉽게 사용할 수 있다고 하여 한번 사용해 봤습니다.
 - 생각보다 이쁘게 안 나와서 실망 ㅠ_ㅠ , 역시 디자이너의 손길이 필요합니다.
upgrader: 스토어에 최신 버전이 있는지를 확인할 수 있는 기능이 필요해서 사용하였습니다.
 -  기능상으로 보면, google play store가 아니라 개인 서버에 package 정보를 올려서 사용할 수 도 있다고 되어있습니다.
 -  나중에 store에 올리지 않고 webstite에서 관리를 하려면 이를 이용하는 것도 좋은 방법일 것 같더군요.
 
 
 
해피코딩!!

반응형

안녕하세요.

 

플러터로 android 용 앱을 개발하다가 앱이 좀 괜찮으면 iOS로 확장하고 싶은 욕구가 생기겠죠?

그런데 처음부터 iOS를 염두해두고 프로젝트를 만들지 않았다면, 확장을 어떻게 해야 하나 할겁니다.

 

간단하게 확장하는 방법 정리 해봤습니다.

 

 

먼저 빌드할 플랫폼을 선택합니다.

 

terminal에서 다음과 같이 입력하면 되는데요.

 $ flutter config --enable-<platform>

 

ios 의 경우에는  flutter config --enable-ios 라고 입력 합니다.

 

그리고 나서 프로젝트를 재 생성 합니다.

$ flutter create .

을 입력하면 다시 프로젝트가 재 생성됩니다.

 

 

 

사용 예 )

~/$ flutter config --enable-ios
Setting "enable-ios" value to "true".

You may need to restart any open editors for them to read new settings.


~/github/arbot_care (karzia)$ flutter create .
⣟

Recreating project ....
  windows/runner/flutter_window.cpp (created)
  windows/runner/utils.h (created)
  windows/runner/utils.cpp (created)
  windows/runner/runner.exe.manifest (created)
  windows/runner/CMakeLists.txt (created)
  windows/runner/win32_window.h (created)
  windows/runner/Runner.rc (created)
  windows/runner/win32_window.cpp (created)
  windows/runner/resources/app_icon.ico (created)
  windows/runner/main.cpp (created)
  windows/runner/resource.h (created)
  windows/runner/flutter_window.h (created)
  windows/flutter/CMakeLists.txt (created)
  windows/.gitignore (created)
  windows/CMakeLists.txt (created)
  arbot_care.iml (created)
  web/favicon.png (created)
  web/index.html (created)
  web/manifest.json (created)
  web/icons/Icon-maskable-512.png (created)
  web/icons/Icon-192.png (created)
  web/icons/Icon-maskable-192.png (created)
  web/icons/Icon-512.png (created)
  android/app/src/main/res/mipmap-mdpi/ic_launcher.png (created)
  android/app/src/main/res/mipmap-hdpi/ic_launcher.png (created)
  android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (created)
  android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (created)
  android/app/src/main/res/mipmap-xhdpi/ic_launcher.png (created)
  android/app/src/main/kotlin/io/netplant/arbot_care/MainActivity.kt (created)
  android/arbot_care_android.iml (created)
  ios/RunnerTests/RunnerTests.swift (created)
  .idea/runConfigurations/main_dart.xml (created)
  .idea/libraries/KotlinJavaRuntime.xml (created)
  linux/main.cc (created)
  linux/my_application.h (created)
  linux/my_application.cc (created)
  linux/flutter/CMakeLists.txt (created)
  linux/.gitignore (created)
  linux/CMakeLists.txt (created)
Resolving dependencies... 
Got dependencies.
Wrote 42 files.

All done!
You can find general documentation for Flutter at: https://docs.flutter.dev/
Detailed API documentation is available at: https://api.flutter.dev/
If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev

 

 

참고로 지원 되는 플랫폼 목록은 다음과 같습니다.

 

--enable-web Enable Flutter for web. This setting will take effect on the master, dev, beta, and stable channels
--no-enable-web Disable Flutter for web. This setting will take effect on the master, dev, beta, and stable channels
--enable-linux-desktop Enable beta-quality support for desktop on Linux. This setting will take effect on the master, dev, beta, and stable channels. Newer beta versions are available on the beta channel
--no-enable-linux-desktop Disable beta-quality support for desktop on Linux. This setting will take effect on the master, dev, beta, and stable channels. Newer beta versions are available on the beta channel
--enable-macos-desktop Enable beta-quality support for desktop on macOS. This setting will take effect on the master, dev, beta, and stable channels. Newer beta versions are available on the beta channel
--no-enable-macos-desktop Disable beta-quality support for desktop on macOS. This setting will take effect on the master, dev, beta, and stable channels. Newer beta versions are available on the beta channel
--enable-windows-desktop Enable beta-quality support for desktop on Windows. This setting will take effect on the master, dev, beta, and stable channels. Newer beta versions are available on the beta channel
--no-enable-windows-desktop Disable beta-quality support for desktop on Windows. This setting will take effect on the master, dev, beta, and stable channels. Newer beta versions are available on the beta channel
--enable-android Enable Flutter for Android. This setting will take effect on the master, dev, beta, and stable channels
--no-enable-android Disable Flutter for Android. This setting will take effect on the master, dev, beta, and stable channels
--enable-ios Enable Flutter for iOS. This setting will take effect on the master, dev, beta, and stable channels
--no-enable-ios Disable Flutter for iOS. This setting will take effect on the master, dev, beta, and stable channels
--enable-fuchsia Enable Flutter for Fuchsia. This setting will take effect on the master channel
--no-enable-fuchsia Disable Flutter for Fuchsia. This setting will take effect on the master channel

 

enable 시킬때는 --enable-<platform> 을 사용하고 disable 시킬때는 --no-enable-<platform> 을 이용합니다.

(disable이 아니네요 ^^;;;)

 

 

 

해피코딩 !!

반응형

안녕하세요.

 

코딩을 하다보면 종종 print를 이용해서 실행(run)창에 로그를 출력하는 경우들이 종종 발생하게 되는데요.

 

이럴때 file 정보와 line 정보가 안보여서 불편할때가 있습니다.

 

요럴때 사용할 수 있는 팁입니다.!!

 

import 'package:flutter/foundation.dart';

dprint(message,{int level=1}){

  if(kDebugMode || level >1) {
    var a = StackTrace.current;
    final regexCodeLine = RegExp(r" (\(.*\))$");
      print("$message${regexCodeLine.stringMatch(a.toString().split("\n")[level])}");
  }
}

 

 

<사용 예제>

  @override
  Widget build(BuildContext context) {

    DatabaseProvider().initMemoryDb();

    dprint("context:width:${context.width} , height:${context.height}");
    
    :

 

 

android studio의 실행창(run) 에 다음과 같이 출력이 됩니다.

그리고 클릭하면 해당 위치로 이동이 되고요.

 

<출력 결과>

I/flutter (22490): context:width:411.4 , height:866.285 (package:sample/ui/IntroPage.dart:32:5)

 

 

!!! 해피 코딩

반응형

안녕하세요.

 

Flutter Bug에 대한 해결책을 정리해볼까 합니다.

https://spikez.tistory.com/456

 

[Flutter] flutter 3.7 google map 버그 (frame이 남아있는 문제)

(2023년1월28일) 어제 시간이 좀 나서 flutter 3.2 버전으로 개발 진행해오던 프로젝트를 3.7로 업그레이드 해봤습니다. 아.. 뭔가 달라진게 있는지 custom 으로 작성한 theme 쪽에서 deprecated 된 것이 보이

spikez.tistory.com

 

 

일단 문제는 위 링크에 있는 내용인데요.

 

화면전환시에 한프래임이 남아서 화면전환을 어색하게 만듭니다.

google map을 dispose 시키기 위해서 여러가지 방법을 사용해봤는데, 방법을 찾지 못하다가, 문제 올린 링크의 내용중에 개발자 한분(AdrienGannerie)이 임시 해결책을 제시해주셨습니다.

 

그래서 그 코드로 테스트 해보고 문제가 사라진것을 확인했네요 ^^

 

코드는 다음과 같습니다.

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

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

  @override
  State<NewMap> createState() => _NewMapState();
}

class _NewMapState extends State<NewMap> {


  late Widget googleMap;
  bool showMap = true;

  late GoogleMapController mapController;

  @override
  void initState() {
    super.initState();
  }


  @override
  void dispose(){
    print("_NewMapState:dispose()");

    mapController.dispose();  // Map 을 Dispose 시키는 부분
    super.dispose();
  }



  @override
  Widget build(BuildContext context) {

    googleMap = GoogleMap(
      mapType: MapType.normal,
      mapToolbarEnabled: false,
      zoomControlsEnabled: false,
      myLocationEnabled: true,
      trafficEnabled: false,
      onMapCreated:  (GoogleMapController controller){
        mapController = controller;
      }, initialCameraPosition: const CameraPosition(target: LatLng(0,0)),

    );
    return PopScope(
        onPopInvoked: (bool didPop) {   //<-- 이부분이 핵심인데, pop 될때 showMap을 false로 변경하고,
            setState(() {  // 상태가 변경되어 google map이 사라지고 빈 container만 남기는것입니다.
            showMap = false; 
          });
        },
        child:showMap?googleMap:Container()
    );   
//Old version : WillPopScope 이 deprecated 되었습니다. ㅠ_ㅠ
//    return WillPopScope(
//        onWillPop: () {   //<-- 이부분이 핵심인데, pop 될때 showMap을 false로 변경하고,
//            setState(() {  // 상태가 변경되어 google map이 사라지고 빈 container만 남기는것입니다.
//            showMap = false; 
//          });
//          return Future.value(true);
//        },
//        child:showMap?googleMap:Container()
//    );
  }
}

 

 

 

위의 WillPopScope 부분이 핵심이 되는데요.

WillPopScope을 걸어서 back navigation 될때 map widget을 build 에서 제거 하는 형태로 문제를 해결한 것입니다.

=> WillPopScope 이 deprecated 됬고 PopScope 을 사용해야 해서 위에 코드에도 반영했습니다.

 

물론 위 코드를 보셔서 알겠지만, 만약 GoogleMap widget이 정상적으로 동작했다면 필요없는 코드입니다.

하지만 당장 앱의 퀄리티를 위해서 ㅠ_ㅠ 개발자가 회피코드(Workaround)를 작성할 수 밖에 없겠네요....

 

저는 이렇게 해서 flutter 3.2를 3.7로 업그레이드 할 수 있게 되었습니다. 

 

 

!!! 해피코딩!! 

 

반응형

Pocket A

https://play.google.com/store/apps/details?id=com.rjlife.pocketa&pli=1

 

Pocket A - Google Play 앱

Chat GPT와 Dall-E 를 활용할 수 있는 AI 어플리케이션입니다.

play.google.com

 

Chat GPT Client 만들기

Chat GPT client 를 만들어서 사용해보기 위해서 만들어보았습니다.

어플리케이션 개발 목적은, flutter 스터디도 있지만, Chat GPT와 같은 대화형 AI 를 다루는 클라이언트(client, application)은 어떻게 만드는 것이 좋을까 고민 해보는 것도 좋을것 같아서 시작하게 되었 습니다.

 

먼저 어플리케이션의 기능 구성을 다음과 같이 고민해봤습니다.

- chat GPT로 채팅하기

- Dall-E 로 그림그리기

이 2가지 기능이 핵심이 될것이고, 부가적으로는 몇가지 기능들을 더 고민해봤습니다.

 

- Dall-E는 영문만 지원이 되기 때문에, chat gpt를 용해서 한글로 입력하면 영문으로 번역하고 나서 입력하는 형태로 하였습니다.

- Chat GPT는 채팅 내용을 기록 하여 여러가지 주제를 따로 관리할수 있도록 하였습니니다.

 

스크린샷

 

 

# 해피 코딩

반응형

Flutter 에서 구글맵을 사용할때 2가지 패키지를 추가해서 사용합니다.

 

google_maps_flutter: ^2.2.3
geocoding: ^2.0.5

 

google map 사용방법은 google cloud service에서 api key를 발급 받고 사용해야합니다.

클라우드 설정이 끝나고 Goecoding을 사용하는 방법은 매우 간단합니다.

 

Geo Coding

geocoding, GeoCode, geocoder등의 용어는  주소를 위도, 경도로 변환하거나 위도,경도로 주소를 넘겨받는 기능을 의미하는 표현으로 많이 사용됩니다.

 

 

주소로  위도, 경도를 받아올때

 

List<Location> locations = await locationFromAddress(address);
Location location = locations[0];
print("locations : ${locations}");

 

위도 경도로, 주소 목록을 받아올때

_placemarks = await placemarkFromCoordinates(location.latitude, location.longitude);
print("placemarks : ${_placemarks}");

 

google geocoding 으로 위도 경도를 입력해서 주소 결과물의 형태는 다음과 같습니다.

2023-05-11 17:02:01.282 1519-1584/com.example I/flutter: placemarks : [
Name: 131, 
          Street: 대한민국 경기도 수원시 팔달구 팔달산로 131, 
          ISO Country Code: KR, 
          Country: 대한민국, 
          Postal code: 16440, 
          Administrative area: 경기도, 
          Subadministrative area: ,
          Locality: 수원시,
          Sublocality: 팔달구,
          Thoroughfare: ,
          Subthoroughfare: 131,       
Name: 16440, 
          Street: 대한민국 경기도 수원시 팔달구, 
          ISO Country Code: KR, 
          Country: 대한민국, 
          Postal code: 16440, 
          Administrative area: 경기도, 
          Subadministrative area: ,
          Locality: 수원시,
          Sublocality: 팔달구,
          Thoroughfare: ,
          Subthoroughfare: ,       
Name: 화서1동, 
          Street: 대한민국 경기도 수원시 팔달구 화서1동, 
          ISO Country Code: KR, 
          Country: 대한민국, 
          Postal code: , 
          Administrative area: 경기도, 
          Subadministrative area: ,
          Locality: 수원시,
          Sublocality: 팔달구,
          :

 

반응형

 

어플리케이션 개발시에 google map을 사용해야 하는 경우 종종 있는데요.

google map 사용시 한가지 불편한 부분에 대한 팁입니다.

 

pubspec.yaml

google_maps_flutter: ^2.2.3

 

GoogleMap(
  mapType: MapType.normal,
  mapToolbarEnabled: false,
  zoomControlsEnabled: false,
  myLocationEnabled: true,
  trafficEnabled: false,
  gestureRecognizers:{
    Factory<OneSequenceGestureRecognizer>(() => EagerGestureRecognizer())
  },
  markers: {Marker(markerId: MarkerId("위치"),position: controller.location.toLatLng())},
  onMapCreated:  (GoogleMapController mapController){
    controller.mapController.complete(mapController);
    },

  onTap: (loc) async {
    controller.location = MapLocation.fromLatLng(loc);
    dprint("loc: (${controller.latitude}, ${controller.longitude})");
    final mc = await controller.mapController.future;
    mc.animateCamera(CameraUpdate.newCameraPosition(CameraPosition(target: loc, zoom: await mc.getZoomLevel())));
    setState(() {});
    WMapUtils.getAddress(loc.latitude, loc.longitude).then((value){
      setState(() {
        controller.addressEditController.text = value;
      });
    });
  },
  initialCameraPosition: CameraPosition(target: controller.location.toLatLng(),zoom: 15),

)),

 

googlemap이 SingleChildScrollView 의 child로 들어갈 경우, pinch zoom, panning 등의 gesture 동작이 Scroller에 우선되어서 googlemap에 전달이 안되는 불상사가 있습니다.

그래서 아래와 같이 GoogleMap에 gestureRecognizers 에 설정을 EagerGestureRecognizer 로 세팅해서 처리하면 google map이 우선되어 동작하게 됩니다.

 

gestureRecognizers:{
  Factory<OneSequenceGestureRecognizer>(() => EagerGestureRecognizer())
},

 

 

해피 코딩!

반응형

Camera를 활용한 기능을 만들고 싶을때가 많은데요.

그럴때를 대비해서 하나 예제를 만들어 두었습니다.

 

먼저 pubspec.yaml에 camera pacakge를 import 합니다.

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  camera:
  get: ^4.6.5

 

아래 코드를 프로젝트에 추가합니다. 

getx 기반으로 작성된 코드입니다.

 

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class TakePhotoPage extends StatefulWidget{



  @override
  State<TakePhotoPage> createState() => _TakePhotoPageState();

  const TakePhotoPage({super.key});
}

class _TakePhotoPageState extends State<TakePhotoPage> {
  late final List<CameraDescription>cameras ;

  CameraController? _controller;
  late var _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    availableCamera();

  }
  void availableCamera() async{
    cameras = await availableCameras();
    if(cameras.isEmpty) return;

    setState(() {
      _controller = CameraController(cameras[0], ResolutionPreset.high);
      _initializeControllerFuture = _controller!.initialize();
    });
  }


  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();

  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      body : SizedBox(
        width: MediaQuery.of(context).size.width,
        height: MediaQuery.of(context).size.height,
        child: _controller == null?Container(): FutureBuilder<void>(
            future: _initializeControllerFuture,
            builder:(context, snapshot){
              if(snapshot.connectionState == ConnectionState.done){
                return CameraPreview(_controller!);
              }else{
                return const Center(child: CircularProgressIndicator());
              }
            }
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      floatingActionButton: FloatingActionButton(
        child:const Icon(Icons.camera_alt),
        onPressed: () async{
          final image = await _controller?.takePicture();
          if(image == null) return;
          // TODO: 사진 촬령후 image를 가지고 하고자 하는 작업 진행
          // ex)
          //   Get.back()  => 이전 화면으로 이동
          //   Get.off( NewPage()); // NewPage()로 이동

        },),
    );
  }
}

 

 

해피 코딩 !!

+ Recent posts