[앱개발] 예보 : 마음을 읽다.

[앱개발] 날씨 기록 - firestore 를 활용하여 사용자의 마음의 날씨를 기록한다.

godofwisdom 2022. 4. 8. 18:40

 

날씨 기록 화면

 

사용자의 현재의 마음의 날씨 상태를 기록 하는 "날씨 기록 페이지" 이다.

 

 

폴더구조 설명 

 

presentation              
       ㄴ weather_write                : 화면기록 폴더
                     ㄴ component      : 화면에서 사용하는 위젯
                     ㄴ view_model     : 화면에 대한 event 및 view 모델
                     ㄴ weather_write_screen.dart : 화면을 그리는 파일

 

 

 

 

 

 

화면에서의 이벤트를 freezed 라이브러리를 사용하여서 정의 한다.

 

 

@freezed
class WeatherWriteEvent with _$WeatherWriteEvent {

  const factory WeatherWriteEvent.onChangeWeather(String code) =
  OnChangeWeather;

  const factory WeatherWriteEvent.insertWeather(WeatherModel weatherModel) =
  InsertWeather;

  const factory WeatherWriteEvent.updateWeather(WeatherModel weatherModel) =
  UpdateWeather;

  const factory WeatherWriteEvent.deleteWeather(WeatherModel weatherModel) =
  DeleteWeather;

  factory WeatherWriteEvent.fromJson(Map<String, dynamic> json) =>
      _$WeatherWriteEventFromJson(json);
}

 

이벤트를 ViewModel에서 구현한다.

/// 이벤트의 기능을 구현하는곳
class WeatherWriteViewModel extends ChangeNotifier {
  final WeatherRepository weatherRepository;

  String _weatherCode = "sun";
  String get weathercode => _weatherCode;

  //여러번 리슨을 해야하는 경우 broadcast 생성자를 사용해야 오류가나지 않는다.
  final _eventcontroller = StreamController<WeatherWriteEvent>.broadcast();
  Stream<WeatherWriteEvent> get eventStream => _eventcontroller.stream;

  WeatherWriteViewModel(this.weatherRepository);

  void onEvent(WeatherWriteEvent event) async {
    event.when(
        insertWeather: _insertWeather,
        updateWeather: _updateWeather,
        deleteWeather: _deleteWeather,
        onChangeWeather: _onChangeWeather);
  }

  Future<void> _onChangeWeather(String code) async {
    _weatherCode = code;
    notifyListeners();
  }

  Future<void> _insertWeather(WeatherModel weatherModel) async {
    await weatherRepository.insertWeather(weatherModel);
  }

  Future<void> _updateWeather(WeatherModel weatherModel) async {
    await weatherRepository.updateWeather(weatherModel);
  }

  Future<void> _deleteWeather(WeatherModel weatherModel) async {
    await weatherRepository.deleteWeather(weatherModel);
  }
}

 

화면에서 유저가 날씨를 클릭할때마다 ViewModel 에서 weatherCode를 관리하여 notifylisteners를 실행 해준다.

String _weatherCode = "sun";
String get weatherCode => _weatherCode;
Future<void> _onChangeWeather(String code) async {
  _weatherCode = code;
  notifyListeners();
}

 

화면에서 유저가 저장을 하였을때에는 ViewModel 에서 StreamController를 통하여서 관리를 한다.

//여러번 리슨을 해야하는 경우 broadcast 생성자를 사용해야 오류가나지 않는다.
final _eventcontroller = StreamController<WeatherWriteUiEvent>.broadcast();
Stream<WeatherWriteUiEvent> get eventStream => _eventcontroller.stream;

사용자가 날씨를 저장할 때, 

Future<void> _insertWeather(WeatherModel weatherModel) async {
  await weatherRepository.insertWeather(weatherModel);

  _eventcontroller.add(const WeatherWriteUiEvent.saveWeather());
}

firestore에 저장할 때, Repository에 등록되어 있는 firebase utils을 호출한다.

Future<void> insertWeather(WeatherModel weatherModel) async {
  firestore
      .collection(collectionName)
      .doc()
      .set(weatherModel.toJson())
      .then((value) => print("weather Added"))
      .catchError((error) => print("Failed to add WeatherModel: $error"));
}

 

 

Screen에서 initState 에 listen 를 등록하여 저장되면 화면이 닫히면서 이전 Navigator를 통하여서 push를 실행한 곳에 true를 넘겨 준다.

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

  Future.microtask((){
    final writeViewModel = context.read<WeatherWriteViewModel>();
    _streamSubscription = writeViewModel.eventStream.listen((event) {
      log("event.toString() : ${event.toString()}");
      event.when(saveWeather: (){
        //true 이면 save 하고 뒤로 넘어갔을 때 true 를 넘긴다.
        Navigator.pop(context, true);
      }, showSnackBar: (String message){
        final snackBar = SnackBar(content: Text(message));
        ScaffoldMessenger.of(context).showSnackBar(snackBar);
      });
    });
  });
}