diff --git a/README.md b/README.md index 60473d0..609b968 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# untitled2 +# Github App A new Flutter project. diff --git a/lib/data/data_sources/user_remote_data_source.dart b/lib/data/data_sources/user_remote_data_source.dart index bd6109d..d2a7ffc 100644 --- a/lib/data/data_sources/user_remote_data_source.dart +++ b/lib/data/data_sources/user_remote_data_source.dart @@ -1,37 +1,27 @@ import 'dart:convert'; +import 'package:get_it/get_it.dart'; import 'package:http/http.dart' as http; +import '../models/user_list_model.dart'; import '../models/user_model.dart'; abstract class UserRemoteDataSource { - Future getUser(int id); - Future> searchUsersByLocation(String location, int page); + Future> searchUsersByLocation(String location, int page); Future getUserDetail(String username); - Future searchUserByUsername(String username); + Future> searchUserByUsername(String name); } class UserRemoteDataSourceImpl implements UserRemoteDataSource { - final http.Client client; + final http.Client client = GetIt.instance(); - UserRemoteDataSourceImpl({required this.client}); + //UserRemoteDataSourceImpl({required this.client}); @override - Future getUser(int id) async { - final response = await client.get(Uri.parse('https://api.github.com/users/$id')); - - if (response.statusCode == 200) { - return UserModel.fromJson(json.decode(response.body)); - } else { - throw Exception('Failed to load User'); - } - } - - @override - Future> searchUsersByLocation(String location, int page) async { + Future> searchUsersByLocation(String location, int page) async { final response = await client.get(Uri.parse('https://api.github.com/search/users?q=location:$location')); if (response.statusCode == 200) { final data = json.decode(response.body); - return (data['items'] as List).map((json) => UserModel.fromJson(json)).toList(); + return (data['items'] as List).map((json) => UserListModel.fromJson(json)).toList(); } else { throw Exception('Failed to load users'); } @@ -49,13 +39,17 @@ class UserRemoteDataSourceImpl implements UserRemoteDataSource { } @override - Future searchUserByUsername(String username) async { - final response = await client.get(Uri.parse('https://api.github.com/users/$username')); + Future> searchUserByUsername(String name) async { + // final response = await client.get(Uri.parse('https://api.github.com/search/users?q=$name')); + final response = await client.get(Uri.parse('https://api.github.com/search/users?q=$name')); + if (response.statusCode == 200) { - return UserModel.fromJson(json.decode(response.body)); + //return UserModel.fromJson(json.decode(response.body)); + final data = json.decode(response.body); + return (data['items'] as List).map((json) => UserListModel.fromJson(json)).toList(); } else { throw Exception('Failed to load User'); } } -} +} \ No newline at end of file diff --git a/lib/data/models/user_list_model.dart b/lib/data/models/user_list_model.dart new file mode 100644 index 0000000..82894a8 --- /dev/null +++ b/lib/data/models/user_list_model.dart @@ -0,0 +1,34 @@ +import '../../domain/entities/user_list_entity.dart'; + +import 'package:json_annotation/json_annotation.dart'; +part 'user_list_model.g.dart'; + +@JsonSerializable() +class UserListModel{ + final String login; + + @JsonKey(name: "avatar_url") + final String avatarUrl; + + @JsonKey(name: "email") + final String? email; + + @JsonKey(name: "type") + final String? type; + + UserListModel( this.login, this.email, this.type, this.avatarUrl); + + factory UserListModel.fromJson(Map json) => _$UserListModelFromJson(json); + + Map toJson() => _$UserListModelToJson(this); + + // Convert User listModel to User list entity + UserListEntity toEntity() { + return UserListEntity( + login: login, + avatarUrl: avatarUrl, + type: type ?? '', + email: email ?? '', + ); + } +} \ No newline at end of file diff --git a/lib/data/models/user_list_model.g.dart b/lib/data/models/user_list_model.g.dart new file mode 100644 index 0000000..2c623c6 --- /dev/null +++ b/lib/data/models/user_list_model.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_list_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserListModel _$UserListModelFromJson(Map json) => + UserListModel( + json['login'] as String, + json['email'] as String?, + json['type'] as String?, + json['avatar_url'] as String, + ); + +Map _$UserListModelToJson(UserListModel instance) => + { + 'login': instance.login, + 'avatar_url': instance.avatarUrl, + 'email': instance.email, + 'type': instance.type, + }; diff --git a/lib/data/models/user_model.dart b/lib/data/models/user_model.dart index cbc8d8a..ae7669f 100644 --- a/lib/data/models/user_model.dart +++ b/lib/data/models/user_model.dart @@ -1,57 +1,64 @@ +import 'package:githubUsers/data/models/user_list_model.dart'; + import '../../domain/entities/user.dart'; +import 'package:json_annotation/json_annotation.dart'; -class UserModel extends User { - UserModel({ - required String login, - required String avatarUrl, - required String name, - required String followers, - required String following, - required String type, - required String bio, - }) : super( - login: login, - avatarUrl: avatarUrl, - name: name, - followers: followers, - following: following, - type: type, - bio: bio, - ); - - factory UserModel.fromJson(Map json) { - return UserModel( - login: json['login'], - avatarUrl: json['avatar_url'], - name: json['name'] ?? '', - followers: json['followers'].toString(), - following: json['following'].toString() , - type: json['type'], - bio: json['bio'] ?? '', - ); - } +part 'user_model.g.dart'; - Map toJson() { - return { - 'login': login, - 'avatar_url': avatarUrl, - 'name': name, - 'followers': followers, - 'following': following, - 'type': type, - 'bio': bio, - }; - } +@JsonSerializable() +class UserModel { + + + final String login; + + @JsonKey(name: "avatar_url") + final String avatarUrl; + + @JsonKey(name: "subscriptions_url") + final String? subscriptionsUrl; + + @JsonKey(name: "repos_url") + final String? reposUrl; + + @JsonKey(name: "name") + final String? name; + @JsonKey(name: "bio") + final String? bio; + + @JsonKey(name: "followers") + final int? followers; + + @JsonKey(name: "following") + final int? following; + + @JsonKey(name: "email") + final String? email; + + @JsonKey(name: "type") + final String? type; + + UserModel(this.login, this.avatarUrl, this.subscriptionsUrl, this.reposUrl, this.name, this.bio, this.followers, this.following, this.email, this.type); + + factory UserModel.fromJson(Map json) => _$UserModelFromJson(json); + + Map toJson() => _$UserModelToJson(this); + + // Convert UserModel to User entity User toEntity() { return User( login: login, avatarUrl: avatarUrl, - name: name, - followers: followers, - following: following, - type: type, - bio: bio, + // url: url ?? '', + subscriptionsUrl: subscriptionsUrl ?? '', + reposUrl: reposUrl ?? '', + name: name ?? '', + type: type ?? '', + followers: followers ?? 0, + following: following ?? 0, + bio: bio ?? '', + email: email ?? '', ); } -} + +} \ No newline at end of file diff --git a/lib/data/models/user_model.g.dart b/lib/data/models/user_model.g.dart new file mode 100644 index 0000000..510c8c9 --- /dev/null +++ b/lib/data/models/user_model.g.dart @@ -0,0 +1,33 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UserModel _$UserModelFromJson(Map json) => UserModel( + json['login'] as String, + json['avatar_url'] as String, + json['subscriptions_url'] as String?, + json['repos_url'] as String?, + json['name'] as String?, + json['bio'] as String?, + (json['followers'] as num?)?.toInt(), + (json['following'] as num?)?.toInt(), + json['email'] as String?, + json['type'] as String?, + ); + +Map _$UserModelToJson(UserModel instance) => { + 'login': instance.login, + 'avatar_url': instance.avatarUrl, + 'subscriptions_url': instance.subscriptionsUrl, + 'repos_url': instance.reposUrl, + 'name': instance.name, + 'bio': instance.bio, + 'followers': instance.followers, + 'following': instance.following, + 'email': instance.email, + 'type': instance.type, + }; diff --git a/lib/data/repositories/user_repository_impl.dart b/lib/data/repositories/user_repository_impl.dart index e9b0b60..390b2eb 100644 --- a/lib/data/repositories/user_repository_impl.dart +++ b/lib/data/repositories/user_repository_impl.dart @@ -1,30 +1,22 @@ import 'package:dartz/dartz.dart'; +import 'package:get_it/get_it.dart'; +import 'package:githubUsers/domain/entities/user_list_entity.dart'; import '../../domain/entities/user.dart'; import '../../domain/repositories/user_repository.dart'; import '../../data/data_sources/user_remote_data_source.dart'; import '../models/user_model.dart'; class UserRepositoryImpl implements UserRepository { - final UserRemoteDataSource remoteDataSource; + final UserRemoteDataSource remoteDataSource = GetIt.instance(); - UserRepositoryImpl({required this.remoteDataSource}); + //UserRepositoryImpl({required this.remoteDataSource}); @override - Future> getUser(int id) async { - try { - final model = await remoteDataSource.getUser(id); - return Right(model.toEntity()); - } catch (e) { - return Left(Exception('Failed to fetch data')); - } - } - - @override - Future>> searchUsersByLocation(String location, int page) async { + Future>> searchUsersByLocation(String location, int page) async { try { final models = await remoteDataSource.searchUsersByLocation(location, page); - final users = models.map((model) => model.toEntity()).toList(); - return Right(users); + final userlist = models.map((model) => model.toEntity()).toList(); + return Right(userlist); } catch (e) { return Left(Exception('Failed to fetch data')); } @@ -41,12 +33,13 @@ class UserRepositoryImpl implements UserRepository { } @override - Future> searchUsersByUsername(String username, int page) async { + Future>> searchUsersByUsername(String name) async { try { - final model = await remoteDataSource.searchUserByUsername(username); - return Right(model.toEntity()); + final models = await remoteDataSource.searchUserByUsername(name); + final users = models.map((model) => model.toEntity()).toList(); + return Right(users); } catch (e) { return Left(Exception('Failed to fetch data')); } } -} +} \ No newline at end of file diff --git a/lib/domain/entities/user.dart b/lib/domain/entities/user.dart index 5ff906a..b1f6055 100644 --- a/lib/domain/entities/user.dart +++ b/lib/domain/entities/user.dart @@ -1,13 +1,14 @@ -// domain/entities/user.dart class User { final String login; final String avatarUrl; + final String reposUrl; + final String subscriptionsUrl; final String name; - final String followers; - final String following; + final int followers; + final int following; final String type; final String bio; - + final String email; User({ required this.login, required this.avatarUrl, @@ -16,5 +17,8 @@ class User { required this.following, required this.type, required this.bio, + required this.reposUrl, + required this.email, + required this.subscriptionsUrl, }); -} +} \ No newline at end of file diff --git a/lib/domain/entities/user_list_entity.dart b/lib/domain/entities/user_list_entity.dart new file mode 100644 index 0000000..595546f --- /dev/null +++ b/lib/domain/entities/user_list_entity.dart @@ -0,0 +1,14 @@ +import 'package:githubUsers/domain/entities/user.dart'; + +class UserListEntity { + final String login; + final String avatarUrl; + final String type; + final String email; + UserListEntity({ + required this.login, + required this.avatarUrl, + required this.type, + required this.email, + }); +} \ No newline at end of file diff --git a/lib/domain/repositories/user_repository.dart b/lib/domain/repositories/user_repository.dart index 4e2156f..5374db1 100644 --- a/lib/domain/repositories/user_repository.dart +++ b/lib/domain/repositories/user_repository.dart @@ -1,10 +1,9 @@ import 'package:dartz/dartz.dart'; +import 'package:githubUsers/domain/entities/user_list_entity.dart'; import '../entities/user.dart'; abstract class UserRepository{ - Future> getUser(int id); - Future>> searchUsersByLocation(String location, int page); + Future>> searchUsersByLocation(String location, int page); Future> getUserDetail(String username); - Future> searchUsersByUsername(String username, int page); -} - + Future>> searchUsersByUsername(String name); +} \ No newline at end of file diff --git a/lib/domain/use_cases/get_user_detail.dart b/lib/domain/use_cases/get_user_detail.dart index 4f05081..ceadb73 100644 --- a/lib/domain/use_cases/get_user_detail.dart +++ b/lib/domain/use_cases/get_user_detail.dart @@ -1,13 +1,14 @@ import 'package:dartz/dartz.dart'; +import 'package:get_it/get_it.dart'; import '../entities/user.dart'; import '../repositories/user_repository.dart'; class GetUserDetail { - final UserRepository repository; + final UserRepository repository = GetIt.instance(); - GetUserDetail(this.repository); + //GetUserDetail(this.repository); Future> call(String username) { return repository.getUserDetail(username); } -} +} \ No newline at end of file diff --git a/lib/domain/use_cases/get_user_name.dart b/lib/domain/use_cases/get_user_name.dart deleted file mode 100644 index 10f5ce5..0000000 --- a/lib/domain/use_cases/get_user_name.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:dartz/dartz.dart'; -import '../entities/user.dart'; -import '../repositories/user_repository.dart'; - -class GetUserName{ - final UserRepository repository; - - GetUserName(this.repository); - - Future> call(int id) async { - return await repository.getUser(id); - } -} diff --git a/lib/domain/use_cases/search_user_by_location.dart b/lib/domain/use_cases/search_user_by_location.dart index 10c718d..e1ac7d2 100644 --- a/lib/domain/use_cases/search_user_by_location.dart +++ b/lib/domain/use_cases/search_user_by_location.dart @@ -1,13 +1,15 @@ import 'package:dartz/dartz.dart'; +import 'package:get_it/get_it.dart'; +import 'package:githubUsers/domain/entities/user_list_entity.dart'; import '../entities/user.dart'; import '../repositories/user_repository.dart'; class SearchUsersByLocation { - final UserRepository repository; + final UserRepository repository = GetIt.instance(); - SearchUsersByLocation(this.repository); + // SearchUsersByLocation(this.repository); - Future>> call(String location, int page) { + Future>> call(String location, int page) { return repository.searchUsersByLocation(location, page); } -} +} \ No newline at end of file diff --git a/lib/domain/use_cases/search_user_by_username.dart b/lib/domain/use_cases/search_user_by_username.dart index 498fcfd..87b5fcd 100644 --- a/lib/domain/use_cases/search_user_by_username.dart +++ b/lib/domain/use_cases/search_user_by_username.dart @@ -1,13 +1,15 @@ import 'package:dartz/dartz.dart'; +import 'package:get_it/get_it.dart'; +import 'package:githubUsers/domain/entities/user_list_entity.dart'; import '../entities/user.dart'; import '../repositories/user_repository.dart'; class SearchUserByUsername { - final UserRepository repository; + final UserRepository repository = GetIt.instance(); - SearchUserByUsername(this.repository); + // SearchUserByUsername(this.repository); - Future> call(String username, int page) { - return repository.searchUsersByUsername(username, page); + Future>> call(String name) { + return repository.searchUsersByUsername(name); } -} +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 0680e58..ef3fde6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:githubUsers/service_locator.dart'; import 'package:provider/provider.dart'; import 'domain/use_cases/get_user_detail.dart'; import 'domain/use_cases/search_user_by_username.dart'; @@ -13,26 +14,25 @@ import 'presentation/providers/user_detail_provider.dart'; import 'presentation/screens/splash_screen.dart'; import 'presentation/screens/user_profile_screen.dart'; import 'presentation/colors/colors.dart'; +import '../../service_locator.dart'; void main() { - final userRemoteDataSource = UserRemoteDataSourceImpl(client: http.Client()); - final userRepository = UserRepositoryImpl(remoteDataSource: userRemoteDataSource); - final searchUsersByLocation = SearchUsersByLocation(userRepository); - final getUserDetail = GetUserDetail(userRepository); - final searchUserByUsername = SearchUserByUsername(userRepository); + + setupLocator(); // Initialize the service locator runApp( MultiProvider( providers: [ ChangeNotifierProvider( - create: (_) => UserProvider(searchUsersByLocation: searchUsersByLocation, searchUsersByUsername: searchUserByUsername), + create: (_) => getIt(), + ), + ChangeNotifierProvider( + create: (_) => getIt(), ), + ChangeNotifierProvider( - create: (_) => InternetProvider(), + create: (_) =>getIt(), ), - // ChangeNotifierProvider( - // create: (_) => UsernameSearchProvider(searchUserByUsername: searchUserByUsername), - // ), ], child: MyApp(), ), @@ -45,9 +45,6 @@ class MyApp extends StatelessWidget { return MaterialApp( debugShowCheckedModeBanner: false, title: 'GitHub User Search', - // theme: ThemeData( - // primarySwatch: Colors.blue, - // ), theme: ThemeData( primaryColor: AppColors.primaryColor, colorScheme: ColorScheme.light( @@ -72,11 +69,6 @@ class MyApp extends StatelessWidget { ), ), home: SplashScreen(), - // routes: { - // '/search_by_username': (context) => UserSearchByUsernameScreen(), - // }, - //home: UserSearchScreen(), ); } -} - +} \ No newline at end of file diff --git a/lib/presentation/providers/internet_provider.dart b/lib/presentation/providers/internet_provider.dart index 3b50b50..02a8096 100644 --- a/lib/presentation/providers/internet_provider.dart +++ b/lib/presentation/providers/internet_provider.dart @@ -15,4 +15,4 @@ class InternetProvider with ChangeNotifier { notifyListeners(); }); } -} +} \ No newline at end of file diff --git a/lib/presentation/providers/user_detail_provider.dart b/lib/presentation/providers/user_detail_provider.dart index 441c55a..e18cf00 100644 --- a/lib/presentation/providers/user_detail_provider.dart +++ b/lib/presentation/providers/user_detail_provider.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import '../../domain/entities/user.dart'; import '../../domain/use_cases/get_user_detail.dart'; class UserDetailProvider extends ChangeNotifier { - final GetUserDetail getUserDetail; + final GetUserDetail getUserDetail = GetIt.instance(); - UserDetailProvider({required this.getUserDetail}); + //UserDetailProvider({required this.getUserDetail}); User? _user; User? get user => _user; @@ -30,4 +31,4 @@ class UserDetailProvider extends ChangeNotifier { _isLoading = false; notifyListeners(); } -} +} \ No newline at end of file diff --git a/lib/presentation/providers/user_provider.dart b/lib/presentation/providers/user_provider.dart index 355c4bc..d25ad8c 100644 --- a/lib/presentation/providers/user_provider.dart +++ b/lib/presentation/providers/user_provider.dart @@ -1,28 +1,31 @@ import 'package:flutter/material.dart'; import 'package:dartz/dartz.dart'; +import 'package:get_it/get_it.dart'; +import 'package:githubUsers/domain/entities/user_list_entity.dart'; import '../../domain/use_cases/search_user_by_location.dart'; -import '../../domain/entities/user.dart'; +//import '../../domain/entities/user.dart'; import 'internet_provider.dart'; import 'package:provider/provider.dart'; import '../../domain/use_cases/search_user_by_username.dart'; class UserProvider with ChangeNotifier { - final SearchUsersByLocation searchUsersByLocation; - final SearchUserByUsername searchUsersByUsername; - List _users = []; + final SearchUsersByLocation searchUsersByLocation = GetIt.instance(); + final SearchUserByUsername searchUsersByUsername = GetIt.instance(); + + List _users = []; String _errorMessage = ''; bool _isLoading = false; bool _isFetchingMore = false; int _page = 1; - List get users => _users; + List get users => _users; String get errorMessage => _errorMessage; bool get isLoading => _isLoading; bool get isFetchingMore => _isFetchingMore; - UserProvider({required this.searchUsersByLocation, required this.searchUsersByUsername}); + // UserProvider({required this.searchUsersByLocation, required this.searchUsersByUsername}); Future searchUsers(BuildContext context, String location) async { @@ -52,7 +55,7 @@ class UserProvider with ChangeNotifier { _isLoading = false; notifyListeners(); } - Future searchUserByUsername(BuildContext context, String username) async { + Future searchUserByUsername(BuildContext context, String name) async { final internetProvider = Provider.of(context, listen: false); if (!internetProvider.isConnected) { _errorMessage = 'No internet connection'; @@ -63,14 +66,14 @@ class UserProvider with ChangeNotifier { _errorMessage = ''; notifyListeners(); - final result = await searchUsersByUsername(username, _page); + final result = await searchUsersByUsername(name); result.fold( (exception) { _errorMessage = exception.toString(); }, (user) { - _users = [user]; + _users = user; }, ); diff --git a/lib/presentation/screens/splash_screen.dart b/lib/presentation/screens/splash_screen.dart index 293c4c4..9abe546 100644 --- a/lib/presentation/screens/splash_screen.dart +++ b/lib/presentation/screens/splash_screen.dart @@ -33,14 +33,6 @@ class _SplashScreenState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - // Text( - // 'GitHub Users', - // style: TextStyle( - // fontSize: 30, - // fontWeight: FontWeight.bold, - // color: Colors.white, - // ), - // ), SizedBox(height: 20), CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Colors.white), @@ -51,7 +43,4 @@ class _SplashScreenState extends State { ), ); } -} - - - +} \ No newline at end of file diff --git a/lib/presentation/screens/user_profile_screen.dart b/lib/presentation/screens/user_profile_screen.dart index 0c3242b..b90665b 100644 --- a/lib/presentation/screens/user_profile_screen.dart +++ b/lib/presentation/screens/user_profile_screen.dart @@ -3,105 +3,119 @@ import 'package:provider/provider.dart'; import '../providers/user_detail_provider.dart'; import '../../domain/entities/user.dart'; import 'package:share/share.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../colors/colors.dart'; -// import 'package:share_plus/share_plus.dart'; class UserProfileScreen extends StatelessWidget { - final User user; + // final User user; - const UserProfileScreen({super.key, required this.user}); + const UserProfileScreen({super.key}); @override Widget build(BuildContext context) { return Consumer( - builder: (context, userDetailProvider, child) { - if (userDetailProvider.isLoading) { - return Scaffold( - appBar: AppBar( - title: Text(user.login), - backgroundColor: AppColors.primaryColor, - ), - body: Center(child: CircularProgressIndicator()), - ); - } else if (userDetailProvider.user == null) { - return Scaffold( - appBar: AppBar( - title: Text(user.login), - backgroundColor: AppColors.primaryColor, - ), - body: Center(child: Text("Failed to load user details")), - ); - } else { - final userDetail = userDetailProvider.user!; - return Scaffold( - appBar: AppBar( - title: Text(userDetail.login), - backgroundColor: AppColors.primaryColor, - // actions: [ - // IconButton( - // icon: Icon(Icons.share), - // onPressed: () { - // Share.share('Check out this GitHub user: ${userDetail.htmlUrl}'); - // }, - // ), - // ], - // actions: [ - // IconButton( - // icon: Icon(Icons.share), - // onPressed: () => _shareUserProfile(userDetail), - // ), - // ], - actions: [ - IconButton( - icon: Icon(Icons.share), - onPressed: () { - Share.share('Check out this GitHub user: ${userDetail.login} - ${userDetail.avatarUrl}'); + builder: (context, userDetailProvider, child) { + if (userDetailProvider.isLoading) { + return Scaffold( + appBar: AppBar( + title: Text("Profile"), + backgroundColor: AppColors.primaryColor, + ), + body: Center(child: CircularProgressIndicator()), + ); + } else if (userDetailProvider.user == null) { + return Scaffold( + appBar: AppBar( + title: Text("Profile"), + backgroundColor: AppColors.primaryColor, + ), + body: Center(child: Text("Failed to load user details")), + ); + } else { + final userDetail = userDetailProvider.user!; + return Scaffold( + appBar: AppBar( + title: Text(userDetail.login), + backgroundColor: AppColors.primaryColor, + actions: [ + IconButton( + icon: Icon(Icons.share), + onPressed: () { + Share.share('Check out this GitHub user: ${userDetail.login} - ${userDetail.avatarUrl}'); + }, + ), + IconButton( + icon: Icon(Icons.open_in_browser), + onPressed: () async { + final url = 'https://github.com/${userDetail.login}'; + if (await canLaunch(url)) { + await launch(url); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Could not open the browser')), + ); + } + }, + ), + ], + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + CircleAvatar( + radius: 50, + backgroundImage: NetworkImage(userDetail.avatarUrl), + ), + SizedBox(height: 16), + Text( + userDetail.name, + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + SizedBox(height: 8), + Text(userDetail.bio ?? 'No bio available'), + Text(userDetail.type), + SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Column( + children: [ + Text('Followers', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(userDetail.followers.toString()), + ], + ), + Column( + children: [ + Text('Following', + style: TextStyle(fontWeight: FontWeight.bold)), + Text(userDetail.following.toString()), + ], + ), + ], + ), + SizedBox(height: 16), + ElevatedButton.icon( + onPressed: () async { + final url = 'https://github.com/${userDetail.login}'; + if (await canLaunch(url)) { + await launch(url); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Could not open the browser')), + ); + } }, + icon: Icon(Icons.open_in_browser), + label: Text('Open GitHub Profile'), ), ], - ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - children: [ - CircleAvatar( - radius: 50, - backgroundImage: NetworkImage(userDetail.avatarUrl), - ), - SizedBox(height: 16), - Text( - userDetail.name, - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), - SizedBox(height: 8), - Text(userDetail.bio ?? 'No bio available'), - Text(userDetail.type), - SizedBox(height: 16), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Column( - children: [ - Text('Followers', - style: TextStyle(fontWeight: FontWeight.bold)), - Text(userDetail.followers.toString()), - ], - ), - Column( - children: [ - Text('Following', - style: TextStyle(fontWeight: FontWeight.bold)), - Text(userDetail.following.toString()), - ], - ), - ], - ), - ], - ), ), - ); - } - }, - ); + ), + ); + } + }, + ); } } diff --git a/lib/presentation/screens/user_search_screen.dart b/lib/presentation/screens/user_search_screen.dart index 9fcf19d..e83839e 100644 --- a/lib/presentation/screens/user_search_screen.dart +++ b/lib/presentation/screens/user_search_screen.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:githubUsers/data/repositories/user_repository_impl.dart'; import 'package:provider/provider.dart'; import '../../data/data_sources/user_remote_data_source.dart'; @@ -8,8 +9,7 @@ import '../providers/user_provider.dart'; import 'user_profile_screen.dart'; import 'package:http/http.dart' as http; import '../colors/colors.dart'; - - +import '../../service_locator.dart'; class UserSearchScreen extends StatefulWidget { @override @@ -21,22 +21,40 @@ class _UserSearchScreenState extends State { final TextEditingController _usernameController = TextEditingController(); bool _isLocationSearch = true; + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + Provider.of(context, listen: false) + .searchUsers(context, _locationController.text); + }); + } + + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('GitHub User Search'), + title: Text('GitHub Users'), backgroundColor: AppColors.primaryColor, ), body: Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ + Text( + 'Search by', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ChoiceChip( - label: Text('Search by Location'), + label: Text('Location'), selected: _isLocationSearch, onSelected: (selected) { setState(() { @@ -46,7 +64,7 @@ class _UserSearchScreenState extends State { ), SizedBox(width: 10), ChoiceChip( - label: Text('Search by Username'), + label: Text('Username'), selected: !_isLocationSearch, onSelected: (selected) { setState(() { @@ -56,7 +74,7 @@ class _UserSearchScreenState extends State { ), ], ), - SizedBox(height: 20), + SizedBox(height: 5), _isLocationSearch ? TextField( controller: _locationController, @@ -123,16 +141,11 @@ class _UserSearchScreenState extends State { context, MaterialPageRoute( builder: (context) => ChangeNotifierProvider( - create: (_) => UserDetailProvider( - getUserDetail: GetUserDetail( - UserRepositoryImpl( - remoteDataSource: - UserRemoteDataSourceImpl( - client: - http.Client()))))..fetchUserDetail(user.login), - child: UserProfileScreen(user: user), + create: (_) => GetIt.instance()..fetchUserDetail(user.login), + child: UserProfileScreen(), + //child: UserProfileScreen(user: user), + ), ), - ), ); }, child: ListTile( @@ -154,113 +167,4 @@ class _UserSearchScreenState extends State { ), ); } -} - - -// class UserSearchScreen extends StatelessWidget { -// final TextEditingController _controller = TextEditingController(); -// final TextEditingController _usernameController = TextEditingController(); -// bool _isLocationSearch = true; -// -// @override -// Widget build(BuildContext context) { -// return Scaffold( -// appBar: AppBar( -// title: Text('GitHub User Search'), -// backgroundColor: AppColors.primaryColor, -// actions: [ -// IconButton( -// icon: Icon(Icons.search), -// onPressed: () { -// Navigator.pushNamed(context, '/search_by_username'); -// }, -// ), -// ], -// ), -// body: Padding( -// padding: const EdgeInsets.all(8.0), -// child: Column( -// children: [ -// TextField( -// controller: _controller, -// decoration: InputDecoration( -// hintText: 'Enter location', -// suffixIcon: IconButton( -// icon: Icon(Icons.search), -// onPressed: () { -// Provider.of(context, listen: false) -// .searchUsers(context, _controller.text); -// }, -// ), -// focusedBorder: OutlineInputBorder( -// borderSide: BorderSide(color: AppColors.primaryColor), -// ), -// ), -// ), -// SizedBox(height: 20), -// Expanded( -// child: Consumer( -// builder: (context, userProvider, child) { -// if (userProvider.isLoading) { -// return Center(child: CircularProgressIndicator()); -// } else if (userProvider.errorMessage.isNotEmpty) { -// return Center(child: Text(userProvider.errorMessage)); -// } else { -// return NotificationListener( -// onNotification: (ScrollNotification scrollInfo) { -// if (scrollInfo.metrics.pixels == -// scrollInfo.metrics.maxScrollExtent && -// !userProvider.isFetchingMore) { -// userProvider.loadMoreUsers(context, _controller.text); -// } -// return false; -// }, -// child: ListView.builder( -// itemCount: userProvider.users.length + -// (userProvider.isFetchingMore ? 1 : 0), -// itemBuilder: (context, index) { -// if (index == userProvider.users.length) { -// return Center(child: CircularProgressIndicator()); -// } -// final user = userProvider.users[index]; -// return InkWell( -// onTap: () { -// Navigator.push( -// context, -// MaterialPageRoute( -// builder: (context) => ChangeNotifierProvider( -// create: (_) => UserDetailProvider( -// getUserDetail: GetUserDetail( -// UserRepositoryImpl( -// remoteDataSource: -// UserRemoteDataSourceImpl( -// client: -// http.Client()))))..fetchUserDetail(user.login), -// child: UserProfileScreen(user: user), -// ), -// ), -// ); -// }, -// child: ListTile( -// leading: CircleAvatar( -// backgroundImage: NetworkImage(user.avatarUrl), -// ), -// title: Text(user.login), -// ), -// ); -// }, -// ), -// ); -// } -// }, -// ), -// ), -// ], -// ), -// ), -// ); -// } -// } - - - +} \ No newline at end of file diff --git a/lib/presentation/widgets/user_list_widget.dart b/lib/presentation/widgets/user_list_widget.dart index 25ab30b..b984991 100644 --- a/lib/presentation/widgets/user_list_widget.dart +++ b/lib/presentation/widgets/user_list_widget.dart @@ -1,4 +1,3 @@ -// presentation/widgets/user_list_widget.dart import 'package:flutter/material.dart'; import '../../domain/entities/user.dart'; import '../screens/user_profile_screen.dart'; @@ -22,7 +21,7 @@ class UserListWidget extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => UserProfileScreen(user: user), + builder: (context) => UserProfileScreen(), ), ); }, @@ -30,6 +29,4 @@ class UserListWidget extends StatelessWidget { }, ); } -} - - +} \ No newline at end of file diff --git a/lib/service_locator.dart b/lib/service_locator.dart new file mode 100644 index 0000000..45d1ec9 --- /dev/null +++ b/lib/service_locator.dart @@ -0,0 +1,42 @@ +import 'package:get_it/get_it.dart'; +import 'package:http/http.dart' as http; +import 'data/data_sources/user_remote_data_source.dart'; +import 'data/repositories/user_repository_impl.dart'; +import 'domain/repositories/user_repository.dart'; +import 'domain/use_cases/get_user_detail.dart'; +import 'domain/use_cases/search_user_by_location.dart'; +import 'domain/use_cases/search_user_by_username.dart'; +import 'presentation/providers/user_detail_provider.dart'; +import 'presentation/providers/user_provider.dart'; +import 'presentation/providers/internet_provider.dart'; + + +final getIt = GetIt.instance; + +void setupLocator() { + // Register InternetProvider as a singleton + getIt.registerLazySingleton(() => InternetProvider()); + + // Register Http Client + getIt.registerLazySingleton(() => http.Client()); + + // Register Data Sources + getIt.registerLazySingleton( + () => UserRemoteDataSourceImpl(), + ); + + // Register Repositories + getIt.registerLazySingleton( + () => UserRepositoryImpl(), + ); + + // Register Use Cases + getIt.registerLazySingleton(() => SearchUsersByLocation()); + getIt.registerLazySingleton(() => GetUserDetail()); + getIt.registerLazySingleton(() => SearchUserByUsername()); + + + // Register Providers + getIt.registerFactory(() => UserProvider()); + getIt.registerFactory(() => UserDetailProvider()); +} \ No newline at end of file diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..f6f23bf 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,10 @@ #include "generated_plugin_registrant.h" +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..f16b4c3 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index be95aee..7b61b93 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,7 +6,9 @@ import FlutterMacOS import Foundation import connectivity_plus_macos +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index b224c29..7f38ec2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" args: dependency: transitive description: @@ -25,6 +41,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" + url: "https://pub.dev" + source: hosted + version: "2.4.11" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe + url: "https://pub.dev" + source: hosted + version: "7.3.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb + url: "https://pub.dev" + source: hosted + version: "8.9.2" characters: dependency: transitive description: @@ -33,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" clock: dependency: transitive description: @@ -41,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" collection: dependency: transitive description: @@ -97,6 +193,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1dceb0cf05cb63a7852c11560060e53ec2f182079a16ced6f4395c5b0875baf8" + url: "https://pub.dev" + source: hosted + version: "3.0.4" cupertino_icons: dependency: "direct main" description: @@ -105,6 +217,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" + source: hosted + version: "2.3.6" dartz: dependency: "direct main" description: @@ -137,6 +257,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -160,6 +296,38 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" http: dependency: "direct main" description: @@ -168,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" http_parser: dependency: transitive description: @@ -176,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -184,6 +368,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + url: "https://pub.dev" + source: hosted + version: "6.8.0" leak_tracker: dependency: transitive description: @@ -216,6 +416,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -264,6 +472,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -288,6 +504,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" provider: dependency: "direct main" description: @@ -296,6 +520,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.2" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + url: "https://pub.dev" + source: hosted + version: "1.3.0" share: dependency: "direct main" description: @@ -304,11 +544,43 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.4" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + url: "https://pub.dev" + source: hosted + version: "1.3.4" source_span: dependency: transitive description: @@ -333,6 +605,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -357,6 +637,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" typed_data: dependency: transitive description: @@ -365,6 +653,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3" + url: "https://pub.dev" + source: hosted + version: "6.3.0" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9" + url: "https://pub.dev" + source: hosted + version: "6.3.8" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + url: "https://pub.dev" + source: hosted + version: "3.1.2" vector_math: dependency: transitive description: @@ -381,6 +733,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.1" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: @@ -389,6 +749,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" + url: "https://pub.dev" + source: hosted + version: "3.0.1" xml: dependency: transitive description: @@ -397,6 +773,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: dart: ">=3.4.4 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 299b600..270c511 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,6 +40,9 @@ dependencies: http: ^1.2.2 dartz: ^0.10.0 share: ^2.0.4 + url_launcher: ^6.0.20 + json_annotation: ^4.8.0 + get_it: ^7.2.0 dev_dependencies: flutter_test: @@ -51,6 +54,8 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^3.0.0 + build_runner: ^2.3.3 + json_serializable: ^6.6.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8083d74..76d495a 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 8cf5d42..f0a74ac 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus_windows + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST