Skip to content

블루베리 템플릿 구현 룰

Jungwoo edited this page Jul 15, 2024 · 1 revision

블루베리 템플릿 구현 룰

개요

이 프로젝트는 다음 규칙을 따라 개발되어야 합니다. 이 규칙은 코드의 일관성과 가독성을 높이며, 유지보수를 용이하게 합니다.

요약

  • 스크린을 먼저 생성합니다.
  • 위젯으로 나눠줍니다. ( 가능하면 StateLess를 사용합니다. )
  • 서버와의 통신이 필요한 경우에는 별도의 다트 파일로 생성하고, 프로바이더 또는 서비스를 연결합니다.
  • 상단에 설명을 꼭 달아줍니다.

규칙

  1. 먼저 기능이 들어갈 상단의 스크린을 생성합니다.
  • 스크린은 StatelessWidget으로 생성합니다. ( 특별한 이유가 없는 한 )
  • 위젯 중 프로바이더, 서비스 연결이 필요한 위젯은 별도의 다트 파일로 분리합니다.
  • 위젯 중 프로바이더, 서비스 연결이 필요 없는 위젯은 하단에 나열합니다.
class ChatScreen extends StatelessWidget {
  const ChatScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text(AppStrings.lessonChatScreenTitle)),
      body: Center(
        child: Column(
          children: [
            Expanded(child: ChatListWidget()),
            ChatSendWidget(),
          ],
        ),
      ),
    );
  }
}
  1. Provider를 사용하는 위젯 ( 데이터를 서버로부터 받아오는 경우 )
  • ConsumerWidget으로 감싸고 Provider를 사용합니다.
  • build 함수 안에 when문을 사용합니다.
  • data, loading, error의 경우에 보여줄 위젯을 하단에 정리합니다.
class ChatListWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final _list = ref.watch(chatListProvider);
    return _list.when(
        data: (data) => _buildListView(data),
        loading: () => const Center(child: CircularProgressIndicator()),
        error: (error, stackTrace) => Center(child: Text('Error: $error')));
  }
}

Widget _buildListView(List<String> data) {
  return ListView.builder(
    shrinkWrap: true,
    itemCount: data.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(data[index]),
      );
    },
  );
}
  1. Provider 구현방법
  • Provider내에서 다른 서비스, 다른 프로바이더를 참고하는 것을 금지합니다.
  • Firebase Repository, Service가 별도로 존재하더라도 프로바이더에는 새로운 instance를 생성 해야합니다.
final chatListProvider = StreamProvider<List<String>>((ref) {
  final firestore = FirebaseFirestore.instance;
  return firestore.collection('chats').snapshots().map((snapshot) {
    return snapshot.docs.map((doc) => doc['message'] as String).toList();
  });
});
  1. Service를 사용하는 위젯 ( 데이터를 서버로 보내기만 하는 경우 )
  • StateLessWidget을 사용합니다.
  • build 안에서 Service를 가져와서 사용합니다.
class ChatSendWidget extends StatelessWidget {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final firebaseService = FirebaseService();

    void _sendChatMessage(String value) async {
      await firebaseService.addChatMessage(value);
      _controller.clear();
    }

    return Container(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _controller,
              decoration: const InputDecoration(
                  hintText: 'Enter message', border: OutlineInputBorder()),
              onSubmitted: (value) async { // 엔터키가 눌러졌을때 하는 행동
                value.isEmpty
                    ? null
                    : _sendChatMessage(value);
              },
            ),
          ),
          IconButton(
            icon: const Icon(Icons.send),
            onPressed: () async { // 눌러졌을때 행동
              _controller.text.isEmpty
                  ? null
                  : _sendChatMessage(_controller.text);
            },
          ),
        ],
      ),
    );
  }
}
  1. Service 구현방법
  • 원하는 Request를 보낼 수 있는 함수를 작성합니다.
  • 하나의 Service에 여러 위젯, 여러 스크린에서 사용되는 함수를 추가해도 괜찮습니다.
class FirebaseService {
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  // ChatScreen.dart
  Future<void> addChatMessage(String message) async {
    try {
      await _firestore.collection('chats').add({
        'message': message,
        'timestamp': DateTime.now(),
      });
    } catch (e) {
      print('Error adding message: $e');
      throw Exception('Failed to add message');
    }
  }
}