Skip to content
This repository has been archived by the owner on Jan 6, 2025. It is now read-only.

Commit

Permalink
feat: add loader during picture capture processing, #13
Browse files Browse the repository at this point in the history
  • Loading branch information
luifr10 committed Mar 4, 2024
1 parent f37ad65 commit 4e4d5cd
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 92 deletions.
35 changes: 35 additions & 0 deletions lib/component/loader.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../constant.dart';

class Loader extends StatelessWidget {
final bool shadowBackground;
final Widget message;

const Loader({
super.key,
this.shadowBackground = false,
required this.message
});

@override
Widget build(BuildContext context) {
return Container(
color: this.shadowBackground ?
Color.fromRGBO(0, 0, 0, 50) :
Colors.transparent
,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LoadingAnimationWidget.staggeredDotsWave(
color: DEFAULT_COLOR,
size: 50,
),
message
],
)
);
}
}
5 changes: 5 additions & 0 deletions lib/constant.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import 'package:flutter/material.dart';

const MaterialColor DEFAULT_COLOR = Colors.indigo;
const String API_HOSTNAME = '10.0.2.2:5000';
const bool API_IS_HTTPS = false;
1 change: 1 addition & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"capture": "Take a picture",
"switchCamera": "Switch camera",
"createSequenceWithPicture_tooltip": "Create a new sequence with captured pictures",
"waitDuringProcessing": "Processing, please wait...",

"newSequenceNameField": "Name",
"newSequenceNameField_placeholder": "Enter the new sequence name",
Expand Down
1 change: 1 addition & 0 deletions lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"capture": "Prendre la une photo",
"switchCamera": "Changer de camera",
"createSequenceWithPicture_tooltip": "Créer une nouvelle séquence avec les photos prises",
"waitDuringProcessing": "Traitement en cours, veuillez patienter...",

"newSequenceNameField": "Nom",
"newSequenceNameField_placeholder": "Saisissez le nom de la nouvelle séquence",
Expand Down
4 changes: 3 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import 'package:go_router/go_router.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:loading_btn/loading_btn.dart';
import 'component/loader.dart';
import 'service/api/api.dart';
import 'constant.dart';

part 'component/app_bar.dart';
part 'component/collection_preview.dart';
Expand All @@ -42,7 +44,7 @@ class PanoramaxApp extends StatelessWidget {
return MaterialApp.router(
title: 'Panoramax',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
colorScheme: ColorScheme.fromSeed(seedColor: DEFAULT_COLOR),
useMaterial3: true,
),
localizationsDelegates: const [
Expand Down
178 changes: 116 additions & 62 deletions lib/page/capture_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ class CapturePage extends StatefulWidget {

class _CapturePageState extends State<CapturePage> {
late CameraController _cameraController;
bool _isProcessing = false;
bool _isRearCameraSelected = true;
List<File> _imgListCaptured = [];
final List<File> _imgListCaptured = [];

@override
void dispose() {
Expand All @@ -31,6 +32,9 @@ class _CapturePageState extends State<CapturePage> {
}

Future takePicture() async {
setState(() {
_isProcessing = true;
});
if (!_cameraController.value.isInitialized) {
return null;
}
Expand All @@ -53,15 +57,15 @@ class _CapturePageState extends State<CapturePage> {
takePicture();
}
} on CameraException catch (e) {
debugPrint('Error occured while taking picture: $e');
debugPrint('Error occurred while taking picture: $e');
return null;
}
}

void addImageToList(XFile rawImage) {
setState(() {
var capturedPicture = new File(rawImage.path);
_imgListCaptured.add(capturedPicture);
_imgListCaptured.add(File(rawImage.path));
_isProcessing = false;
});
}

Expand Down Expand Up @@ -106,72 +110,122 @@ class _CapturePageState extends State<CapturePage> {
);
return Stack(
children: [
(_cameraController.value.isInitialized)
? CameraPreview(_cameraController)
: Container(
color: Colors.transparent,
child: const Center(child: CircularProgressIndicator())),
new Positioned(
bottom: height,
left: 0,
child: new Container(
width: MediaQuery.of(context).size.width,
height: height,
decoration: new BoxDecoration(color: Colors.transparent),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Expanded(
child: IconButton(
onPressed: takePicture,
iconSize: 100,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
icon: const Icon(Icons.circle_outlined, color: Colors.white),
tooltip: AppLocalizations.of(context)!.capture
)),
]),
)
),
new Positioned(
cameraPreview(),
captureButton(height, context),
Positioned(
bottom: 0,
left: 0,
child: new Container(
child: Container(
width: MediaQuery.of(context).size.width,
height: height,
decoration: new BoxDecoration(color: Colors.black),
decoration: const BoxDecoration(color: Colors.black),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Expanded(
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 30,
icon: Icon(
_isRearCameraSelected
? CupertinoIcons.switch_camera
: CupertinoIcons.switch_camera_solid,
color: Colors.white),
onPressed: () {
setState(
() => _isRearCameraSelected = !_isRearCameraSelected);
initCamera(widget.cameras![_isRearCameraSelected ? 0 : 1]);
},
tooltip: AppLocalizations.of(context)!.switchCamera
)),
_imgListCaptured.length > 0 ? badges.Badge(
badgeContent: Text('${_imgListCaptured.length}'),
child: cartIcon,
): cartIcon,
Expanded(
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 30,
icon: Icon(Icons.send_outlined,
color: Colors.white),
onPressed: goToCollectionCreationPage,
tooltip: AppLocalizations.of(context)!.createSequenceWithPicture_tooltip
)),
switchCameraButton(context),
imageCart(cartIcon),
createSequenceButton(context),
]),
)
)
),
if(_isProcessing) processingLoader(context)
]
);
}

Expanded switchCameraButton(BuildContext context) {
return Expanded(
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 30,
icon: Icon(
_isRearCameraSelected
? CupertinoIcons.switch_camera
: CupertinoIcons.switch_camera_solid,
color: Colors.white),
onPressed: () {
setState(
() => _isRearCameraSelected = !_isRearCameraSelected);
initCamera(widget.cameras![_isRearCameraSelected ? 0 : 1]);
},
tooltip: AppLocalizations.of(context)!.switchCamera
)
);
}

Expanded createSequenceButton(BuildContext context) {
return Expanded(
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 30,
icon: const Icon(
Icons.send_outlined,
color: Colors.white
),
onPressed: goToCollectionCreationPage,
tooltip: AppLocalizations.of(context)!.createSequenceWithPicture_tooltip
)
);
}

Widget imageCart(IconButton cartIcon) {
return _imgListCaptured.isNotEmpty ?
badges.Badge(
badgeContent: Text('${_imgListCaptured.length}'),
child: cartIcon,
):
cartIcon;
}

Positioned captureButton(double height, BuildContext context) {
return Positioned(
bottom: height,
left: 0,
child: Container(
width: MediaQuery.of(context).size.width,
height: height,
decoration: const BoxDecoration(color: Colors.transparent),
child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [
Expanded(
child: IconButton(
onPressed: takePicture,
iconSize: 100,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
icon: const Icon(Icons.circle_outlined, color: Colors.white),
tooltip: AppLocalizations.of(context)!.capture
)),
]),
)
);
}

StatelessWidget cameraPreview() {
return _cameraController.value.isInitialized
? CameraPreview(_cameraController)
: Container(
color: Colors.transparent,
child: const Center(child: CircularProgressIndicator()
)
);
}

Positioned processingLoader(BuildContext context) {
return Positioned(
top: 0,
bottom: 0,
left: 0,
right: 0,
child: Loader(
message: DefaultTextStyle(
style: Theme.of(context).textTheme.bodyLarge!,
child: Text(
AppLocalizations.of(context)!.waitDuringProcessing,
style: const TextStyle(
color: Colors.white,
),
),
),
shadowBackground: true
)
);
}
}
2 changes: 1 addition & 1 deletion lib/page/collection_creation_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CollectionCreationPage extends StatefulWidget {
class _CarouselWithIndicatorState extends State<CollectionCreationPage> {
int _current = 0;
final CarouselController _carouselController = CarouselController();
final collectionNameTextController = TextEditingController();
final collectionNameTextController = TextEditingController(text: 'My collection ${DateFormat(DATE_FORMATTER).format(new DateTime.now())}');
final _formKey = GlobalKey<FormState>();

@override
Expand Down
45 changes: 23 additions & 22 deletions lib/page/homepage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,27 +69,26 @@ class _HomePageState extends State<HomePage> {
child: Scaffold(
appBar: PanoramaxAppBar(context: context),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
margin: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Semantics(
header: true,
child: Text(
AppLocalizations.of(context)!.yourSequence,
style: GoogleFonts.nunito(
fontSize: 25,
fontWeight: FontWeight.w400
)
),
)
),
SizedBox(
height: 690,
child: displayBody(isLoading)
)
]
child: Stack(
children: <Widget>[
Container(
margin: const EdgeInsets.fromLTRB(10, 10, 10, 10),
child: Semantics(
header: true,
child: Text(
AppLocalizations.of(context)!.yourSequence,
style: GoogleFonts.nunito(
fontSize: 25,
fontWeight: FontWeight.w400
)
),
)
),
SizedBox(
height: 720,
child: displayBody(isLoading)
)
]
),
),
floatingActionButton: FloatingActionButton(
Expand Down Expand Up @@ -136,7 +135,9 @@ class LoaderIndicatorView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Text(AppLocalizations.of(context)!.loading)
child: Loader(
message: Text(AppLocalizations.of(context)!.loading),
),
)
]
);
Expand Down
4 changes: 1 addition & 3 deletions lib/service/api/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import 'package:http/http.dart' as http;
import 'dart:convert';
import 'model/geo_visio.dart';
import 'dart:io';
import '../../constant.dart';

part 'endpoint/collections_api.dart';

const String API_HOSTNAME = '10.0.2.2:5000';
const bool API_IS_HTTPS = false;
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
loading_animation_widget:
dependency: "direct main"
description:
name: loading_animation_widget
sha256: "1901682600273a966c34cf44a85fc5355da92a8d08a8a43c11adc4e471993e3a"
url: "https://pub.dev"
source: hosted
version: "1.2.0+4"
loading_btn:
dependency: "direct main"
description:
Expand Down
Loading

0 comments on commit 4e4d5cd

Please sign in to comment.