코딩/♠♠기술 구현

닉네임 변경시 친구목록의 친구에게 알림 가도록 하기.

흑백 개발자 2024. 8. 22. 18:49
728x90

전체 과정 설명 및 관련 페이지별 수정 사항

아래는 닉네임 변경 시 친구 목록에 알림 메시지가 전송되도록 구현한 전체 과정과 관련된 각 페이지에서 어떤 부분이 수정되었는지에 대한 설명입니다.

1. notification_service.dart - 알림 관리 서비스

  • 역할: 친구 기기에 알림 메시지를 전송하고, 알림 메시지를 로컬 저장소에 저장하는 기능을 제공합니다.
  • 수정 사항:
    • sendNicknameChangeNotification: 닉네임 변경 시 알림 메시지를 전송하는 메서드.
    • storeNotification: 받은 알림 메시지를 로컬 저장소에 저장하는 메서드.
    • getStoredNotifications: 저장된 알림 메시지를 불러오는 메서드.
    • clearNotifications: 저장된 알림 메시지를 모두 삭제하는 메서드.

2. sharedpreference.dart - 로컬 저장소 관리

  • 역할: 알림 메시지를 로컬 저장소에 저장하고 불러오는 기능을 제공합니다.
  • 수정 사항:
    • storeNotification: 알림 메시지를 SharedPreferences에 저장.
    • getStoredNotifications: SharedPreferences에서 저장된 알림 메시지 목록을 불러옴.
    • clearNotifications: 저장된 알림 메시지를 삭제.

3. profile_provider.dart - 프로필 관련 데이터 관리

  • 역할: 닉네임 변경과 관련된 데이터를 관리하고, 닉네임 변경 시 친구들에게 알림 메시지를 보내는 기능을 담당합니다.
  • 수정 사항:
    • updateNickname: 닉네임을 변경하고, 알림을 전송하는 로직을 추가.
    • notifyFriendsAboutNicknameChange: 닉네임이 변경된 후 친구들에게 알림을 전송하는 메서드.
    • getFriendIds: 친구 목록을 불러오는 메서드 (이 메서드는 실제 구현할 때 오류가 발생할 수 있습니다).

4. nickname_change.dart - 닉네임 변경 화면

  • 역할: 사용자가 닉네임을 변경할 수 있는 UI를 제공합니다.
  • 수정 사항:
    • onPressed 이벤트 핸들러에서 닉네임 변경 요청을 ProfileProvider로 전달하고, 닉네임이 변경된 후 알림을 전송하도록 수정.

5. user_provider.dart - 사용자 관리

  • 역할: 사용자 정보 관련 데이터를 관리하고, 데이터베이스와의 상호작용을 담당합니다.
  • 수정 사항:
    • 닉네임 변경 후 알림 메시지를 친구들에게 전송하는 로직이 추가되었습니다.

순차적으로 사용된 클래스들 정리

  1. NotificationService (notification_service.dart)
    • 알림 메시지를 전송하고 로컬에 저장하는 기능을 제공.
  2. SharedPreferences (sharedpreference.dart)
    • 로컬 저장소에 알림 메시지를 저장하고 불러오는 기능을 제공.
  3. ProfileProvider (profile_provider.dart)
    • 프로필과 관련된 데이터를 관리하며, 닉네임 변경 시 알림을 전송.
  4. NicknameChange (nickname_change.dart)
    • 사용자가 닉네임을 변경할 수 있는 UI 제공 및 닉네임 변경 이벤트 처리.
  5. UserProvider (user_provider.dart)
    • 사용자 정보를 관리하고 친구 목록과 관련된 기능을 담당.

이 과정에서 닉네임 변경 시 친구에게 알림을 보내고, 알림을 로컬에 저장하여 사용자가 나중에 알림을 확인할 수 있도록 하는 기능을 구현했습니다. 각 단계별로 페이지를 수정하며 위의 클래스들을 연동하여 기능을 완성했습니다.

 

 

 

 

1. notification_service.dart

이 클래스는 Flutter Local Notifications와 SharedPreferences를 사용하여 알림을 생성하고 저장하는 역할을 담당합니다.

 

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:shared_preferences/shared_preferences.dart';

class NotificationService {
  static final FlutterLocalNotificationsPlugin _notificationsPlugin =
      FlutterLocalNotificationsPlugin();

  static Future<void> sendNicknameChangeNotification(
      {required String oldNickname, required String newNickname}) async {
    // 알림 생성
    const AndroidNotificationDetails androidDetails =
        AndroidNotificationDetails(
      'nickname_change_channel',
      'Nickname Change',
      channelDescription: '닉네임 변경 알림',
      importance: Importance.max,
      priority: Priority.high,
    );

    const NotificationDetails platformDetails =
        NotificationDetails(android: androidDetails);

    // 알림 전송
    await _notificationsPlugin.show(
      0,
      '닉네임 변경',
      '$oldNickname에서 $newNickname으로 닉네임이 변경되었습니다.',
      platformDetails,
    );

    // 로컬 저장소에 메시지 저장
    final prefs = await SharedPreferences.getInstance();
    List<String> notifications =
        prefs.getStringList('notifications') ?? [];
    notifications.add('$oldNickname에서 $newNickname으로 닉네임이 변경되었습니다.');
    await prefs.setStringList('notifications', notifications);
  }

  // 저장된 알림 가져오기
  static Future<List<String>> getStoredNotifications() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getStringList('notifications') ?? [];
  }

  // 알림 지우기
  static Future<void> clearNotifications() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove('notifications');
  }
}

 

2. profile_provider.dart

이 클래스는 프로필 관련 데이터를 관리하며, 닉네임 변경 시 친구들에게 알림을 보내는 역할을 수행합니다.

 

import 'package:flutter/foundation.dart';
import 'package:todomate/models/signup_model.dart';
import 'package:todomate/service/notification_service.dart';

class ProfileProvider with ChangeNotifier {
  final DatabaseHelper _dbHelper = DatabaseHelper(); // DB 불러옴
  String? _nickname;
  String _avatarPath = 'asset/image/avata_1.png'; // 기본 아바타 경로로 초기화
  bool _isNicknameLoaded = false; // 닉네임 로딩 상태 추가
  int _friendCount = 0;

  String? get nickname => _nickname;
  String get avatarPath => _avatarPath;
  bool get isNicknameLoaded => _isNicknameLoaded;
  int get friendCount => _friendCount;

  Future<void> loadNickname(String loginId) async {
    if (_isNicknameLoaded) return;
    _nickname = await _dbHelper.getNickname(loginId);
    if (_nickname == null || _nickname!.isEmpty) {
      _nickname = loginId;
      await _dbHelper.updateNickname(loginId, _nickname!);
    }
    _isNicknameLoaded = true;
    notifyListeners();
  }

  Future<void> updateNickname(String loginId, String newNickname) async {
    String oldNickname = _nickname!;
    await _dbHelper.updateNickname(loginId, newNickname);
    _nickname = newNickname;

    // 친구들에게 알림 전송
    await notifyFriendsAboutNicknameChange(loginId, oldNickname, newNickname);

    notifyListeners();
  }

  Future<void> notifyFriendsAboutNicknameChange(
      String loginId, String oldNickname, String newNickname) async {
    List<String> friendIds = await _dbHelper.getFriendIds(loginId);
    for (String friendId in friendIds) {
      await NotificationService.sendNicknameChangeNotification(
          oldNickname: oldNickname, newNickname: newNickname);
    }
  }

  void updateFriendCount(int count) {
    _friendCount = count;
    notifyListeners();
  }
}

 

3. nickname_change.dart

이 클래스는 사용자가 닉네임을 변경할 수 있는 UI를 제공합니다. 닉네임 변경 시 ProfileProvider의 updateNickname 메서드를 호출하여 친구들에게 알림을 보냅니다.

 

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todomate/screens/my/profile_provider.dart';
import 'package:todomate/screens/my/profile_widget.dart';

class NicknameChange extends StatelessWidget {
  final String loginId;
  final String nickname;
  final TextEditingController _nicknameController = TextEditingController();

  NicknameChange({required this.loginId, required this.nickname});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          backgroundColor: Colors.grey,
          leading: IconButton(
            icon: Icon(Icons.arrow_back, color: Colors.black),
            onPressed: () {
              Navigator.pop(context);
            },
          ),
        ),
        resizeToAvoidBottomInset: false,
        body: SafeArea(
          child: Column(
            children: [
              ProfileWidget(nickname: nickname),
              Expanded(
                child: Container(
                  color: Colors.white,
                  padding: EdgeInsets.symmetric(horizontal: 16.0),
                  child: Column(
                    children: [
                      SizedBox(height: 40.0),
                      TextField(
                        controller: _nicknameController,
                        decoration: InputDecoration(
                          hintText: 'New NickName',
                          hintStyle: TextStyle(
                            color: Colors.grey,
                            fontSize: 35.0,
                            fontWeight: FontWeight.normal,
                          ),
                          filled: true,
                          fillColor: Colors.white,
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(12.0),
                            borderSide: BorderSide.none,
                          ),
                          contentPadding: EdgeInsets.zero,
                          prefixIcon: Icon(
                            Icons.published_with_changes,
                            size: 40.0,
                            color: Colors.grey,
                          ),
                        ),
                        style: TextStyle(
                          color: Colors.black,
                          fontSize: 24.0,
                          fontWeight: FontWeight.bold,
                        ),
                        textAlign: TextAlign.center,
                      ),
                      SizedBox(height: 50.0),
                      ElevatedButton(
                        onPressed: () {
                          context.read<ProfileProvider>().updateNickname(
                              loginId, _nicknameController.text);
                        },
                        style: ElevatedButton.styleFrom(
                          backgroundColor: Colors.white,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(12.0),
                          ),
                          elevation: 5,
                          shadowColor: Colors.grey.withOpacity(0.3),
                          padding: EdgeInsets.symmetric(
                              horizontal: 70.0, vertical: 10.0),
                        ),
                        child: Text(
                          'Change',
                          style: TextStyle(
                            fontSize: 35.0,
                            color: Colors.grey,
                          ),
                        ),
                      ),
                      SizedBox(height: 120.0),
                      ElevatedButton(
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        style: ElevatedButton.styleFrom(
                          backgroundColor: Colors.orange,
                          shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(12.0),
                          ),
                          padding: EdgeInsets.symmetric(
                              horizontal: 70.0, vertical: 10.0),
                        ),
                        child: Text(
                          'Confirm',
                          style: TextStyle(
                            fontSize: 35.0,
                            color: Colors.white,
                          ),
                        ),
                      ),
                      SizedBox(height: 60.0),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

 

4. sharedpreference.dart

이 클래스는 SharedPreferences를 사용하여 알림 메시지를 저장하고 관리하는 역할을 수행합니다.

 

import 'package:shared_preferences/shared_preferences.dart';

class SharedPreferencesHelper {
  static Future<void> storeNotification(String message) async {
    final prefs = await SharedPreferences.getInstance();
    List<String> notifications =
        prefs.getStringList('notifications') ?? [];
    notifications.add(message);
    await prefs.setStringList('notifications', notifications);
  }

  static Future<List<String>> getStoredNotifications() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getStringList('notifications') ?? [];
  }

  static Future<void> clearNotifications() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove('notifications');
  }
}

 

5. user_provider.dart

코드 리뷰:

import 'package:flutter/foundation.dart';
import 'package:todomate/models/signup_model.dart';

class UserProvider with ChangeNotifier {
  DatabaseHelper _dbHelper = DatabaseHelper();

  Future<void> registerUser(Map<String, dynamic> user) async {
    await _dbHelper.insertUser(user);
    notifyListeners();
  }

  Future<Map<String, dynamic>?> loginUser(String loginId, String password) async {
    return await _dbHelper.loginUser(loginId, password);
  }

  Future<void> sendFriendRequest(String userId, String friendId) async {
    await _dbHelper.sendFriendRequest(userId, friendId);
    notifyListeners();
  }

  Future<void> acceptFriendRequest(String userId, String friendId) async {
    await _dbHelper.acceptFriendRequest(userId, friendId);
    notifyListeners();
  }

  Future<List<Map<String, dynamic>>> searchUsers(String query, String userId) async {
    return await _dbHelper.searchUsers(query, userId);
  }

  Future<List<Map<String, dynamic>>> getFriendRequests(String userId) async {
    return await _dbHelper.getFriendRequests(userId);
  }
}

 

728x90