Browse Source

add: loading logic and extract data

pull/9/head
AmirrezaChegini 7 days ago
parent
commit
2fbbddb8a5
  1. 2
      lib/core/routers/my_routes.dart
  2. 54
      lib/features/intro/data/datasource/intro_datasource.dart
  3. 21
      lib/features/intro/data/repository_impl/intro_repository_impl.dart
  4. 2
      lib/features/intro/domain/repository/intro_repository.dart
  5. 16
      lib/features/intro/domain/usecases/extract_data_usecase.dart
  6. 11
      lib/features/intro/domain/usecases/loading_stream_usecase.dart
  7. 28
      lib/features/intro/presentation/bloc/intro_bloc.dart
  8. 1
      lib/features/intro/presentation/bloc/intro_event.dart
  9. 5
      lib/features/intro/presentation/ui/intro_page.dart
  10. 15
      lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart
  11. 4
      lib/init_bindings.dart
  12. 8
      pubspec.lock
  13. 1
      pubspec.yaml

2
lib/core/routers/my_routes.dart

@ -31,7 +31,7 @@ GoRouter get appPages => GoRouter(
name: Routes.introPage, name: Routes.introPage,
path: Routes.introPage, path: Routes.introPage,
builder: (context, state) => BlocProvider( builder: (context, state) => BlocProvider(
create: (context) => IntroBloc(locator()),
create: (context) => IntroBloc(locator(), locator(), locator()),
child: const IntroPage(), child: const IntroPage(),
), ),
), ),

54
lib/features/intro/data/datasource/intro_datasource.dart

@ -1,6 +1,9 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_archive/flutter_archive.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/network/http_request.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.dart';
@ -8,15 +11,19 @@ import 'package:hadi_hoda_flutter/core/response/base_response.dart';
import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart'; import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
abstract class IIntroDatasource { abstract class IIntroDatasource {
Future<void> saveLevels(); Future<void> saveLevels();
Future<void> extractData();
Stream<double> loadingStream();
} }
class IntroDatasourceImpl implements IIntroDatasource { class IntroDatasourceImpl implements IIntroDatasource {
final IHttpRequest httpRequest; final IHttpRequest httpRequest;
final StreamController<double> streamController = StreamController<double>.broadcast();
const IntroDatasourceImpl(this.httpRequest);
IntroDatasourceImpl(this.httpRequest);
@override @override
Future<void> saveLevels() async { Future<void> saveLevels() async {
@ -38,4 +45,49 @@ class IntroDatasourceImpl implements IIntroDatasource {
throw MyException(errorMessage: '$e'); throw MyException(errorMessage: '$e');
} }
} }
@override
Future<void> extractData() async {
try {
final Directory dir = await getApplicationDocumentsDirectory();
final File file = File('${dir.path}/data.zip');
if (!(await file.exists())) {
final ByteData assetFile = await rootBundle.load('assets/data/data.zip');
await file.create(recursive: true);
await file.writeAsBytes(
assetFile.buffer.asUint8List(
assetFile.offsetInBytes,
assetFile.lengthInBytes,
),
flush: true,
);
await ZipFile.extractToDirectory(
zipFile: file,
destinationDir: dir,
onExtracting: (zipEntry, progress) {
streamController.add(progress);
return ZipFileOperation.includeItem;
},
);
} else {
streamController.add(20);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(40);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(60);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(80);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(100);
await Future.delayed(Duration(milliseconds: 150));
}
} catch (e) {
throw MyException(errorMessage: '$e');
}
}
@override
Stream<double> loadingStream() => streamController.stream;
} }

21
lib/features/intro/data/repository_impl/intro_repository_impl.dart

@ -25,4 +25,25 @@ class IntroRepositoryImpl implements IIntroRepository {
} }
} }
} }
@override
Future<DataState<NoParams, MyException>> extractData() async {
try {
await datasource.extractData();
return DataState.success(NoParams());
} on MyException catch (e) {
return DataState.error(e);
} catch (e) {
if (kDebugMode) {
rethrow;
} else {
return DataState.error(MyException(errorMessage: '$e'));
}
}
}
@override
Stream<double> loadingStream() {
return datasource.loadingStream();
}
} }

2
lib/features/intro/domain/repository/intro_repository.dart

@ -4,4 +4,6 @@ import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
abstract class IIntroRepository { abstract class IIntroRepository {
Future<DataState<NoParams, MyException>> saveLevels(); Future<DataState<NoParams, MyException>> saveLevels();
Future<DataState<NoParams, MyException>> extractData();
Stream<double> loadingStream();
} }

16
lib/features/intro/domain/usecases/extract_data_usecase.dart

@ -0,0 +1,16 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/usecase/usecase.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
class ExtractDataUseCase implements UseCase<NoParams, NoParams> {
final IIntroRepository repository;
const ExtractDataUseCase(this.repository);
@override
Future<DataState<NoParams, MyException>> call(NoParams params) {
return repository.extractData();
}
}

11
lib/features/intro/domain/usecases/loading_stream_usecase.dart

@ -0,0 +1,11 @@
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
class LoadingStreamUseCase {
final IIntroRepository repository;
const LoadingStreamUseCase(this.repository);
Stream<double> call() {
return repository.loadingStream();
}
}

28
lib/features/intro/presentation/bloc/intro_bloc.dart

@ -4,6 +4,8 @@ import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart'; import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/extract_data_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart';
@ -12,14 +14,21 @@ class IntroBloc extends Bloc<IntroEvent, IntroState> {
/// ------------constructor------------ /// ------------constructor------------
IntroBloc( IntroBloc(
this._saveLevelsUseCase, this._saveLevelsUseCase,
this._extractDataUseCase,
this._loadingStreamUseCase,
) : super(const IntroState()) { ) : super(const IntroState()) {
on<SaveLevelsEvent>(_saveLevelsEvent); on<SaveLevelsEvent>(_saveLevelsEvent);
on<ExtractDataEvent>(_extractDataEvent);
loadingStream = _loadingStreamUseCase();
} }
/// ------------UseCases------------ /// ------------UseCases------------
final SaveLevelsUseCase _saveLevelsUseCase; final SaveLevelsUseCase _saveLevelsUseCase;
final ExtractDataUseCase _extractDataUseCase;
final LoadingStreamUseCase _loadingStreamUseCase;
/// ------------Variables------------ /// ------------Variables------------
Stream<double> loadingStream = Stream.empty();
/// ------------Controllers------------ /// ------------Controllers------------
@ -29,6 +38,21 @@ class IntroBloc extends Bloc<IntroEvent, IntroState> {
FutureOr<void> _saveLevelsEvent(SaveLevelsEvent event, FutureOr<void> _saveLevelsEvent(SaveLevelsEvent event,
Emitter<IntroState> emit) async { Emitter<IntroState> emit) async {
await _saveLevelsUseCase(NoParams()).then( await _saveLevelsUseCase(NoParams()).then(
(value) {
value.fold(
(data) async {
add(ExtractDataEvent());
},
(error) {},
);
},
);
}
FutureOr<void> _extractDataEvent(ExtractDataEvent event,
Emitter<IntroState> emit) async {
await _extractDataUseCase(NoParams()).then(
(value) { (value) {
value.fold( value.fold(
(data) async { (data) async {
@ -38,7 +62,9 @@ class IntroBloc extends Bloc<IntroEvent, IntroState> {
}, },
); );
}, },
(error) {},
(error) {
print(error.errorMessage);
},
); );
}, },
); );

1
lib/features/intro/presentation/bloc/intro_event.dart

@ -3,3 +3,4 @@ sealed class IntroEvent {
} }
class SaveLevelsEvent extends IntroEvent {} class SaveLevelsEvent extends IntroEvent {}
class ExtractDataEvent extends IntroEvent {}

5
lib/features/intro/presentation/ui/intro_page.dart

@ -57,7 +57,10 @@ class _IntroPageState extends State<IntroPage> {
Positioned _loading(BuildContext context) { Positioned _loading(BuildContext context) {
return Positioned( return Positioned(
bottom: MediaQuery.viewPaddingOf(context).bottom, bottom: MediaQuery.viewPaddingOf(context).bottom,
child: IntroLoadingWidget(percent: 80),
child: IntroLoadingWidget(
percent: 80,
loadingStream: context.read<IntroBloc>().loadingStream,
),
); );
} }
} }

15
lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart

@ -7,9 +7,11 @@ class IntroLoadingWidget extends StatelessWidget {
const IntroLoadingWidget({ const IntroLoadingWidget({
super.key, super.key,
this.percent, this.percent,
this.loadingStream,
}); });
final double? percent; final double? percent;
final Stream<double>? loadingStream;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -32,7 +34,12 @@ class IntroLoadingWidget extends StatelessWidget {
], ],
), ),
), ),
child: Row(
child: StreamBuilder<double>(
initialData: 0,
stream: loadingStream,
builder: (context, snapshot) {
print(snapshot.data);
return Row(
children: [ children: [
Expanded( Expanded(
flex: 85, flex: 85,
@ -41,7 +48,7 @@ class IntroLoadingWidget extends StatelessWidget {
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 300), duration: const Duration(milliseconds: 300),
padding: EdgeInsetsDirectional.only( padding: EdgeInsetsDirectional.only(
end: 260 - ((percent ?? 0) * 260 / 100),
end: 260 - ((snapshot.data ?? 0) * 260 / 100),
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Color(0xFF1F59BD).withValues(alpha: 0.25), color: Color(0xFF1F59BD).withValues(alpha: 0.25),
@ -67,7 +74,7 @@ class IntroLoadingWidget extends StatelessWidget {
flex: 15, flex: 15,
child: Center( child: Center(
child: Text( child: Text(
'${percent?.toInt() ?? 0}%',
'${snapshot.data?.toInt() ?? 0}%',
style: MyTextStyle.normal17.copyWith( style: MyTextStyle.normal17.copyWith(
color: Color(0XFF6E83A8), color: Color(0XFF6E83A8),
), ),
@ -75,6 +82,8 @@ class IntroLoadingWidget extends StatelessWidget {
), ),
), ),
], ],
);
}
), ),
), ),
); );

4
lib/init_bindings.dart

@ -10,6 +10,8 @@ import 'package:hadi_hoda_flutter/features/home/domain/usecases/get_home_usecase
import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart'; import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart';
import 'package:hadi_hoda_flutter/features/intro/data/repository_impl/intro_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/intro/data/repository_impl/intro_repository_impl.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/extract_data_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart'; import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart';
import 'package:hadi_hoda_flutter/features/level/data/repository_impl/level_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/level/data/repository_impl/level_repository_impl.dart';
@ -46,6 +48,8 @@ void initBindings() {
locator.registerLazySingleton<IIntroDatasource>(() => IntroDatasourceImpl(locator())); locator.registerLazySingleton<IIntroDatasource>(() => IntroDatasourceImpl(locator()));
locator.registerLazySingleton<IIntroRepository>(() => IntroRepositoryImpl(locator())); locator.registerLazySingleton<IIntroRepository>(() => IntroRepositoryImpl(locator()));
locator.registerLazySingleton<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator())); locator.registerLazySingleton<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator()));
locator.registerLazySingleton<ExtractDataUseCase>(() => ExtractDataUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
/// Home Feature /// Home Feature
locator.registerLazySingleton<IHomeDatasource>(() => HomeDatasourceImpl(locator())); locator.registerLazySingleton<IHomeDatasource>(() => HomeDatasourceImpl(locator()));

8
pubspec.lock

@ -254,6 +254,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_archive:
dependency: "direct main"
description:
name: flutter_archive
sha256: "5ca235f304c12bf468979235f400f79846d204169d715939e39197106f5fc970"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
flutter_bloc: flutter_bloc:
dependency: "direct main" dependency: "direct main"
description: description:

1
pubspec.yaml

@ -13,6 +13,7 @@ dependencies:
equatable: ^2.0.7 equatable: ^2.0.7
flutter: flutter:
sdk: flutter sdk: flutter
flutter_archive: ^6.0.3
flutter_bloc: ^9.1.1 flutter_bloc: ^9.1.1
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter

Loading…
Cancel
Save