Merge pull request #47 from Iconica-Development/feature/local_service

feat: local service
This commit is contained in:
Gorter-dev 2024-01-30 15:55:48 +01:00 committed by GitHub
commit a6550fd57a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 390 additions and 223 deletions

1
.gitignore vendored
View file

@ -19,6 +19,7 @@ migrate_working_dir/
*.ipr
*.iws
.idea/
ios
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line

View file

@ -1,3 +1,7 @@
## 1.1.0
- Added LocalChatService for example app
## 1.0.0
- Added pagination for the ChatDetailScreen

View file

@ -15,6 +15,7 @@ migrate_working_dir/
*.ipr
*.iws
.idea/
ios
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line

View file

@ -0,0 +1,16 @@
# example
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View file

@ -1,17 +1,9 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_chat/flutter_chat.dart';
import 'package:flutter_chat_firebase/flutter_chat_firebase.dart';
void main(List<String> args) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
// Set your firebase app here
// options: FirebaseOptions(apiKey: 'apiKey', appId: 'appId', messagingSenderId: 'messagingSenderId', projectId: 'projectId')
);
runApp(const App());
}
@ -26,50 +18,13 @@ class App extends StatelessWidget {
}
}
class Home extends StatefulWidget {
class Home extends StatelessWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
@override
void initState() {
FirebaseAuth.instance.signInWithEmailAndPassword(
email: 'your email', password: 'your password');
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Align(
alignment: Alignment.bottomRight,
child: ChatEntryWidget(
chatService: FirebaseChatService(),
onTap: _onTap,
),
),
],
),
),
);
}
void _onTap() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => chatNavigatorUserStory(
ChatUserStoryConfiguration(
chatService: FirebaseChatService(),
chatOptionsBuilder: (ctx) => const ChatOptions(),
),
context),
),
return Center(
child: chatNavigatorUserStory(context),
);
}
}

View file

@ -16,6 +16,8 @@ dependencies:
path: ../
flutter_chat_firebase:
path: ../../flutter_chat_firebase
flutter_chat_local:
path: ../../flutter_chat_local
dev_dependencies:
flutter_test:

View file

@ -0,0 +1,14 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
expect(true, true);
});
}

View file

@ -4,9 +4,10 @@
library flutter_chat;
export 'package:flutter_chat_view/flutter_chat_view.dart';
export 'package:flutter_chat_interface/flutter_chat_interface.dart';
export 'package:flutter_chat/src/routes.dart';
export 'package:flutter_chat/src/models/chat_configuration.dart';
export 'package:flutter_chat/src/flutter_chat_userstory.dart';
export 'package:flutter_chat/src/chat_entry_widget.dart';
export 'package:flutter_chat/src/flutter_chat_navigator_userstory.dart';
export 'package:flutter_chat/src/flutter_chat_userstory.dart';
export 'package:flutter_chat/src/models/chat_configuration.dart';
export 'package:flutter_chat/src/routes.dart';
export 'package:flutter_chat_interface/flutter_chat_interface.dart';
export 'package:flutter_chat_view/flutter_chat_view.dart';

View file

@ -1,12 +1,13 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_chat_view/flutter_chat_view.dart';
import 'package:flutter_chat/flutter_chat.dart';
import 'package:flutter_chat_local/local_chat_service.dart';
class ChatEntryWidget extends StatefulWidget {
const ChatEntryWidget({
required this.chatService,
required this.onTap,
this.chatService,
this.onTap,
this.widgetSize = 75,
this.backgroundColor = Colors.grey,
this.icon = Icons.chat,
@ -16,11 +17,11 @@ class ChatEntryWidget extends StatefulWidget {
super.key,
});
final ChatService chatService;
final ChatService? chatService;
final Color backgroundColor;
final double widgetSize;
final Color counterBackgroundColor;
final Function() onTap;
final Function()? onTap;
final IconData icon;
final Color iconColor;
final TextStyle? textStyle;
@ -30,13 +31,32 @@ class ChatEntryWidget extends StatefulWidget {
}
class _ChatEntryWidgetState extends State<ChatEntryWidget> {
ChatService? chatService;
@override
void initState() {
super.initState();
chatService ??= widget.chatService ?? LocalChatService();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => widget.onTap.call(),
onTap: () =>
widget.onTap?.call() ??
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => chatNavigatorUserStory(
context,
configuration: ChatUserStoryConfiguration(
chatService: chatService!,
chatOptionsBuilder: (ctx) => const ChatOptions(),
),
),
),
),
child: StreamBuilder<int>(
stream:
widget.chatService.chatOverviewService.getUnreadChatsCountStream(),
stream: chatService!.chatOverviewService.getUnreadChatsCountStream(),
builder: (BuildContext context, snapshot) {
return Stack(
alignment: Alignment.center,

View file

@ -4,10 +4,20 @@
import 'package:flutter/material.dart';
import 'package:flutter_chat/flutter_chat.dart';
import 'package:flutter_chat_local/service/local_chat_service.dart';
Widget chatNavigatorUserStory(
ChatUserStoryConfiguration configuration, BuildContext context) {
return _chatScreenRoute(configuration, context);
BuildContext context, {
ChatUserStoryConfiguration? configuration,
}) {
return _chatScreenRoute(
configuration ??
ChatUserStoryConfiguration(
chatService: LocalChatService(),
chatOptionsBuilder: (ctx) => const ChatOptions(),
),
context,
);
}
Widget _chatScreenRoute(

View file

@ -4,7 +4,7 @@
name: flutter_chat
description: A new Flutter package project.
version: 1.0.0
version: 1.1.0
publish_to: none
@ -20,12 +20,17 @@ dependencies:
git:
url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_view
ref: 1.0.0
ref: 1.1.0
flutter_chat_interface:
git:
url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface
ref: 1.0.0
ref: 1.1.0
flutter_chat_local:
git:
url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_local
ref: 1.1.0
dev_dependencies:
flutter_lints: ^2.0.0

View file

@ -4,7 +4,7 @@
name: flutter_chat_firebase
description: A new Flutter package project.
version: 1.0.0
version: 1.1.0
publish_to: none
environment:
@ -23,7 +23,7 @@ dependencies:
git:
url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface
ref: 1.0.0
ref: 1.1.0
dev_dependencies:
flutter_lints: ^2.0.0

View file

@ -4,7 +4,7 @@
name: flutter_chat_interface
description: A new Flutter package project.
version: 1.0.0
version: 1.1.0
publish_to: none
environment:
@ -17,7 +17,7 @@ dependencies:
flutter_data_interface:
git:
url: https://github.com/Iconica-Development/flutter_data_interface.git
ref: 1.0.0
ref: 1.1.0
dev_dependencies:
flutter_lints: ^2.0.0

View file

@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,6 @@
library local_chat_service;
export 'service/local_chat_service.dart';
export 'service/local_chat_detail_service.dart';
export 'service/local_chat_overview_service.dart';
export 'service/local_chat_user_service.dart';

View file

@ -0,0 +1,113 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_chat_interface/flutter_chat_interface.dart';
import 'package:flutter_chat_local/local_chat_service.dart';
class LocalChatDetailService with ChangeNotifier implements ChatDetailService {
final ChatOverviewService chatOverviewService;
LocalChatDetailService({required this.chatOverviewService});
final List<ChatMessageModel> _cumulativeMessages = [];
final StreamController<List<ChatMessageModel>> _controller =
StreamController<List<ChatMessageModel>>.broadcast();
late StreamSubscription? _subscription;
@override
Future<void> fetchMoreMessage(int pageSize, String chatId) async {
await chatOverviewService.getChatById(chatId).then((value) {
_cumulativeMessages.clear();
_cumulativeMessages.addAll(value.messages!);
_controller.add(_cumulativeMessages);
});
notifyListeners();
return Future.value();
}
@override
List<ChatMessageModel> getMessages() {
return _cumulativeMessages;
}
@override
Stream<List<ChatMessageModel>> getMessagesStream(String chatId) {
_controller.onListen = () {
_subscription =
chatOverviewService.getChatById(chatId).asStream().listen((event) {
_cumulativeMessages.clear();
_cumulativeMessages.addAll(event.messages!);
_controller.add(_cumulativeMessages);
});
};
return _controller.stream;
}
@override
Future<void> sendImageMessage(
{required String chatId, required Uint8List image}) async {
var chat = (chatOverviewService as LocalChatOverviewService)
.chats
.firstWhere((element) => element.id == chatId);
var message = ChatImageMessageModel(
sender: ChatUserModel(
id: "3",
firstName: "ico",
lastName: "nica",
imageUrl: "https://picsum.photos/100/200",
),
timestamp: DateTime.now(),
imageUrl: "https://picsum.photos/200/300",
);
await (chatOverviewService as LocalChatOverviewService).updateChat(
chat.copyWith(
messages: [...chat.messages!, message],
lastMessage: message,
lastUsed: DateTime.now(),
),
);
chat.messages?.add(message);
_cumulativeMessages.add(message);
notifyListeners();
return Future.value();
}
@override
Future<void> sendTextMessage(
{required String chatId, required String text}) async {
var chat = (chatOverviewService as LocalChatOverviewService)
.chats
.firstWhere((element) => element.id == chatId);
var message = ChatTextMessageModel(
sender: ChatUserModel(
id: "3",
firstName: "ico",
lastName: "nica",
imageUrl: "https://picsum.photos/100/200",
),
timestamp: DateTime.now(),
text: text,
);
await (chatOverviewService as LocalChatOverviewService).updateChat(
chat.copyWith(
messages: [...chat.messages!, message],
lastMessage: message,
lastUsed: DateTime.now(),
),
);
chat.messages?.add(message);
_cumulativeMessages.add(message);
notifyListeners();
return Future.value();
}
@override
void stopListeningForMessages() {
_subscription?.cancel();
_subscription = null;
}
}

View file

@ -0,0 +1,75 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter_chat_interface/flutter_chat_interface.dart';
class LocalChatOverviewService
with ChangeNotifier
implements ChatOverviewService {
final List<PersonalChatModel> _chats = [];
List<PersonalChatModel> get chats => _chats;
final StreamController<List<ChatModel>> _chatsController =
StreamController<List<ChatModel>>.broadcast();
Future<void> updateChat(ChatModel chat) {
var index = _chats.indexWhere((element) => element.id == chat.id);
_chats[index] = chat as PersonalChatModel;
_chatsController.addStream(Stream.value(_chats));
notifyListeners();
return Future.value();
}
@override
Future<void> deleteChat(ChatModel chat) {
_chats.removeWhere((element) => element.id == chat.id);
_chatsController.add(_chats);
notifyListeners();
return Future.value();
}
@override
Future<ChatModel> getChatById(String id) {
return Future.value(_chats.firstWhere((element) => element.id == id));
}
@override
Future<ChatModel> getChatByUser(ChatUserModel user) {
PersonalChatModel? chat;
try {
chat = _chats.firstWhere((element) => element.user.id == user.id);
} catch (e) {
chat = PersonalChatModel(
user: user,
messages: [],
id: '',
);
chat.id = chat.hashCode.toString();
_chats.add(chat);
}
_chatsController.add([..._chats]);
notifyListeners();
return Future.value(chat);
}
@override
Stream<List<ChatModel>> getChatsStream() {
return _chatsController.stream;
}
@override
Stream<int> getUnreadChatsCountStream() {
return Stream.value(0);
}
@override
Future<void> readChat(ChatModel chat) async {
return Future.value();
}
@override
Future<ChatModel> storeChatIfNot(ChatModel chat) {
return Future.value(chat);
}
}

View file

@ -0,0 +1,34 @@
import 'package:flutter_chat_interface/flutter_chat_interface.dart';
import 'package:flutter_chat_local/service/local_chat_detail_service.dart';
import 'package:flutter_chat_local/service/local_chat_overview_service.dart';
import 'package:flutter_chat_local/service/local_chat_user_service.dart';
class LocalChatService implements ChatService {
LocalChatService({
this.localChatDetailService,
this.localChatOverviewService,
this.localChatUserService,
}) {
{
localChatOverviewService ??= LocalChatOverviewService();
localChatDetailService ??= LocalChatDetailService(
chatOverviewService: localChatOverviewService!,
);
localChatUserService ??= LocalChatUserService();
}
}
ChatDetailService? localChatDetailService;
ChatOverviewService? localChatOverviewService;
ChatUserService? localChatUserService;
@override
ChatDetailService get chatDetailService => localChatDetailService!;
@override
ChatOverviewService get chatOverviewService => localChatOverviewService!;
@override
ChatUserService get chatUserService => localChatUserService!;
}

View file

@ -0,0 +1,33 @@
import 'package:flutter_chat_interface/flutter_chat_interface.dart';
class LocalChatUserService implements ChatUserService {
List<ChatUserModel> users = [
ChatUserModel(
id: '1',
firstName: 'John',
lastName: "Doe",
imageUrl: 'https://picsum.photos/200/300',
),
ChatUserModel(
id: '2',
firstName: 'Jane',
lastName: "Doe",
imageUrl: 'https://picsum.photos/200/300',
),
];
@override
Future<List<ChatUserModel>> getAllUsers() {
return Future.value(users);
}
@override
Future<ChatUserModel?> getCurrentUser() {
return Future.value(ChatUserModel());
}
@override
Future<ChatUserModel?> getUser(String id) {
var user = users.firstWhere((element) => element.id == id);
return Future.value(user);
}
}

View file

@ -0,0 +1,24 @@
name: flutter_chat_local
description: "A new Flutter package project."
version: 1.1.0
publish_to: none
homepage:
environment:
sdk: ">=3.2.5 <4.0.0"
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
flutter_chat_interface:
git:
url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface
ref: 1.1.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:

View file

@ -1,14 +0,0 @@
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/opt/homebrew/Caskroom/flutter/3.10.2/flutter
FLUTTER_APPLICATION_PATH=/Users/mikedoornenbal/Documents/iconica/flutter_community_chat/packages/flutter_chat_view
COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_TARGET=lib/main.dart
FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=1.0.0
FLUTTER_BUILD_NUMBER=1.0.0
EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386
EXCLUDED_ARCHS[sdk=iphoneos*]=armv7
DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true
TREE_SHAKE_ICONS=false
PACKAGE_CONFIG=.dart_tool/package_config.json

View file

@ -1,13 +0,0 @@
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/opt/homebrew/Caskroom/flutter/3.10.2/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/mikedoornenbal/Documents/iconica/flutter_community_chat/packages/flutter_chat_view"
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_TARGET=lib/main.dart"
export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1.0.0"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.dart_tool/package_config.json"

View file

@ -1,41 +0,0 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end

View file

@ -1,19 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#ifndef GeneratedPluginRegistrant_h
#define GeneratedPluginRegistrant_h
#import <Flutter/Flutter.h>
NS_ASSUME_NONNULL_BEGIN
@interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end
NS_ASSUME_NONNULL_END
#endif /* GeneratedPluginRegistrant_h */

View file

@ -1,63 +0,0 @@
//
// Generated file. Do not edit.
//
// clang-format off
#import "GeneratedPluginRegistrant.h"
#if __has_include(<cloud_firestore/FLTFirebaseFirestorePlugin.h>)
#import <cloud_firestore/FLTFirebaseFirestorePlugin.h>
#else
@import cloud_firestore;
#endif
#if __has_include(<firebase_auth/FLTFirebaseAuthPlugin.h>)
#import <firebase_auth/FLTFirebaseAuthPlugin.h>
#else
@import firebase_auth;
#endif
#if __has_include(<firebase_core/FLTFirebaseCorePlugin.h>)
#import <firebase_core/FLTFirebaseCorePlugin.h>
#else
@import firebase_core;
#endif
#if __has_include(<firebase_storage/FLTFirebaseStoragePlugin.h>)
#import <firebase_storage/FLTFirebaseStoragePlugin.h>
#else
@import firebase_storage;
#endif
#if __has_include(<image_picker_ios/FLTImagePickerPlugin.h>)
#import <image_picker_ios/FLTImagePickerPlugin.h>
#else
@import image_picker_ios;
#endif
#if __has_include(<path_provider_foundation/PathProviderPlugin.h>)
#import <path_provider_foundation/PathProviderPlugin.h>
#else
@import path_provider_foundation;
#endif
#if __has_include(<sqflite/SqflitePlugin.h>)
#import <sqflite/SqflitePlugin.h>
#else
@import sqflite;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[FLTFirebaseFirestorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseFirestorePlugin"]];
[FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]];
[FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]];
[FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]];
[FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]];
[PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]];
[SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]];
}
@end

View file

@ -10,7 +10,6 @@ export 'src/components/chat_row.dart';
export 'src/config/chat_options.dart';
export 'src/config/chat_translations.dart';
export 'src/screens/chat_detail_screen.dart';
export 'src/screens/chat_entry_widget.dart';
export 'src/screens/chat_profile_screen.dart';
export 'src/screens/chat_screen.dart';
export 'src/screens/new_chat_screen.dart';

View file

@ -4,7 +4,7 @@
name: flutter_chat_view
description: A standard flutter package.
version: 1.0.0
version: 1.1.0
publish_to: none
@ -20,7 +20,7 @@ dependencies:
git:
url: https://github.com/Iconica-Development/flutter_chat
path: packages/flutter_chat_interface
ref: 1.0.0
ref: 1.1.0
cached_network_image: ^3.2.2
flutter_image_picker:
git: