Hi Everyone I wanted to show video uploading progress bar continuously like 0,1,2………100%
here is code which I implemented but it didn’t work as I want
I want to show progress bar continuously 0,1,2,………100%
Concise – To implement a video uploading progress bar that increments continuously from 0% to 100%, along with handling two API calls, you can follow this approach:
Requirements:
Progress Bar Behavior:
The progress bar should increment continuously (from 0% to 100%) while both APIs are working.
It should move steadily until the video upload is done, then move to 100% when the second API (training data) is called and completed.
Two APIs:
API 1: Upload the video file.
API 2: Upload training data once the video file is successfully uploade
Controller code -
import 'package:get/get.dart';
import 'package:interkashi/modules/components/extension/get_extension.dart';
import 'package:interkashi/modules/feature/academy/model/training_video_upload_model.dart';
import 'package:interkashi/modules/feature/academy/service/upload_video_service.dart';
class UploadVideoController extends GetxController {
final UploadVideoService _uploadVideoService;
UploadVideoController(this._uploadVideoService);
Rxn<String> selectedVideoPath = Rxn<String>();
RxBool isUploading = RxBool(false);
RxDouble uploadProgress = RxDouble(0.0);
Rx<bool?> uploadSuccess = Rx<bool?>(null);
final double maxSizeMB = 100;
RxBool flag = true.obs;
late String lessonId;
late String courseId;
Timer? progressTimer;
@override
void onInit() {
super.onInit();
final arguments = Get.arguments;
lessonId = arguments['lessonId'];
courseId = arguments['courseId'];
}
@override
void onClose() {
progressTimer?.cancel();
super.onClose();
}
Future<void> checkFileSize(String? video) async {
if (video == null) return;
File videoFile = File(video);
int fileSize = videoFile.lengthSync();
double mxSize = fileSize / (1024 * 1024);
if (mxSize > maxSizeMB) {
flag.value = false;
GetExt.snackBar("Please Upload less than 100MB file");
} else {
selectedVideoPath.value = video;
flag.value = true;
}
}
void startProgressBar() {
// Start moving the progress bar slowly until it reaches 40%.
progressTimer = Timer.periodic(Duration(milliseconds: 300), (timer) {
if (uploadProgress.value < 0.4) {
uploadProgress.value += 0.01; // Slow increment until 40%
} else {
timer.cancel(); // Stop increment at 40%, wait for first API
}
});
}
Future<void> uploadVideo() async {
if (selectedVideoPath.value == null || !flag.value) {
return;
}
uploadSuccess.value = null;
isUploading.value = true;
uploadProgress.value = 0.0;
startProgressBar(); // Start initial progress bar
// First API: Upload the video file
final uploadResponse = await _uploadVideoService.uploadVideo(
selectedVideoPath.value!,
(progress) {
// Progress for the first API (video upload) but don't let it exceed 70%
if (progress <= 0.4) {
uploadProgress.value = progress * 0.4; // Increment up to 40%
}
},
);
if (uploadResponse != null) {
// First API succeeded, jump progress to 70%
uploadProgress.value = 0.7;
// Second API: Upload the training data
final trainingResponse = await _uploadVideoService.uploadTrainingData(
TrainingVideoUploadModel(
lessonId: lessonId,
courseId: courseId,
videoUrl: uploadResponse.video.url,
thumbnailUrl: uploadResponse.thumbnail.url,
),
);
if (trainingResponse != null) {
// Gradually increase progress to 100% as the second API completes
while (uploadProgress.value < 1.0) {
await Future.delayed(Duration(milliseconds: 100)); // Small delay for smooth progress
uploadProgress.value += 0.03; // Gradually increase from 70% to 100%
}
uploadProgress.value = 1.0;
await Future.delayed(Duration(milliseconds: 500));
uploadSuccess.value = true;
} else {
// Second API failed
uploadSuccess.value = false;
GetExt.snackBar("Failed to upload training data");
}
} else {
// First API failed
uploadSuccess.value = false;
GetExt.snackBar("Failed to upload video");
}
selectedVideoPath.value = null;
isUploading.value = false;
progressTimer?.cancel();
}
}
Provider -
import 'dart:io';
import 'package:get/get_connect/connect.dart';
import 'package:http/http.dart' as http;
import 'package:interkashi/modules/components/provider/base_provider.dart';
import 'package:interkashi/modules/feature/academy/model/training_video_upload_model.dart';
class UploadVideoProvider extends BaseProvider {
Future<http.Response> uploadVideo(File file) async {
return await uploadFile("/files/videoAndThumbnailUpload", file);
}
Future<Response> uploadTrainingData(TrainingVideoUploadModel trainingVideoModel) async {
String url = "/training/upload";
final body = {
'lessonId': trainingVideoModel.lessonId,
'courseId': trainingVideoModel.courseId,
'videoUrl': trainingVideoModel.videoUrl,
'thumbnailUrl': trainingVideoModel.thumbnailUrl,
};
final response = await post(url, body);
return response;
}
}
Service -
import 'dart:convert';
import 'dart:io';
import 'package:interkashi/modules/components/service/base_service.dart';
import 'package:interkashi/modules/feature/academy/model/training_video_upload_model.dart';
import 'package:interkashi/modules/feature/academy/model/upload_video_model.dart';
import 'package:interkashi/modules/feature/academy/provider/upload_video_provider.dart';
class UploadVideoService extends BaseService {
final UploadVideoProvider _uploadVideoProvider;
UploadVideoService(this._uploadVideoProvider);
Future<UploadVideoModel?> uploadVideo(String filePath,Function(double) onProgress) async {
try {
final response = await _uploadVideoProvider.uploadVideo(File(filePath));
return UploadVideoModel.fromJson(jsonDecode(response.body));
} catch (e) {
print("Error during video upload: $e");
return null;
}
}
Future<TrainingVideoUploadModel?> uploadTrainingData(TrainingVideoUploadModel trainingVideoModel) async {
try {
final response = await _uploadVideoProvider.uploadTrainingData(trainingVideoModel);
if (response.statusCode == 200) {
if (response.body is Map<String, dynamic>) {
return TrainingVideoUploadModel.fromJson(response.body);
} else {
return TrainingVideoUploadModel.fromJson(jsonDecode(response.body));
}
} else {
return null;
}
} catch (e) {
print('Error during training data upload: $e');
return null;
}
}
}
Page Code -
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:interkashi/modules/components/buttons/buttons.dart';
import 'package:interkashi/modules/components/extension/get_extension.dart';
import 'package:interkashi/modules/components/theme/kashi_theme.dart';
import 'package:interkashi/modules/feature/academy/controller/upload_video_controller.dart';
import 'package:interkashi/modules/feature/academy/widget/upload_done_widget.dart';
import 'package:interkashi/modules/feature/academy/widget/upload_failed_widget.dart';
import 'package:interkashi/modules/routes/kashi_routes.dart';
import 'package:lottie/lottie.dart';
class VideoUploadingProgressPage extends StatefulWidget {
@override
State<VideoUploadingProgressPage> createState() => _VideoUploadingProgressPageState();
}
class _VideoUploadingProgressPageState extends State<VideoUploadingProgressPage> with TickerProviderStateMixin {
final UploadVideoController _controller = Get.find<UploadVideoController>();
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 10),
)..repeat(reverse: false, period: Duration(seconds: 10));
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Obx(() {
if (_controller.uploadSuccess.value != null) {
if (_controller.uploadSuccess.value!) {
return UploadDoneWidget();
} else {
return UploadFailedWidget();
}
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Lottie.asset(
'assets/lottie/uploading.json',
controller: _animationController,
animate: true,
repeat: false,
reverse: false,
width: MediaQuery.sizeOf(context).height * 0.3,
fit: BoxFit.cover,
),
SizedBox(height: 60),
Obx(() {
final progressPercentage = (_controller.uploadProgress.value * 100).toInt();
return Text(
'$progressPercentage%',
style: KashiTheme.sh2SemiBold.copyWith(
color: KashiColors.whiteFA,
),
);
}),
SizedBox(height: 8),
Padding(
padding: EdgeInsets.only(left: 20, right: 17),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: KashiColors.grey3,
borderRadius: BorderRadius.circular(6),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Obx(() {
return LinearProgressIndicator(
value: _controller.uploadProgress.value,
backgroundColor: Colors.transparent,
color: KashiColors.green2,
minHeight: 12,
);
}),
),
),
),
SizedBox(height: 15),
Text(
'Uploading your Training...',
style: KashiTheme.h3SemiBold
.copyWith(
fontWeight: FontWeight.w500,
color: KashiColors.whiteFA,
)
.toDMSanFontsFamily(),
textAlign: TextAlign.center,
),
SizedBox(height: 16),
Text(
'Please wait while we upload your training video.',
style: KashiTheme.sh2SemiBold
.copyWith(
fontWeight: FontWeight.w500,
color: KashiColors.grey6,
)
.toDMSanFontsFamily(),
textAlign: TextAlign.center,
),
],
);
}),
bottomNavigationBar: Padding(
padding: const EdgeInsets.only(left: 20, right: 20, bottom: 16),
child: Obx(() {
final isUploadComplete =
_controller.uploadProgress.value == 1.0 && (_controller.uploadSuccess.value ?? false);
final isUploadFailed = (_controller.uploadSuccess.value == false);
final buttonText = (_controller.uploadSuccess.value ?? true) ? "Done" : "Try Upload Again";
final buttonAction = (isUploadComplete || isUploadFailed)
? () {
if (_controller.uploadSuccess.value ?? false) {
GetExt.snackBar('Video uploaded successfully');
Get.until((route) => route.settings.name == KashiRoutes.home);
} else {
Get.until((route) => route.settings.name == KashiRoutes.uploadVideoPage);
}
}
: null;
final buttonBackgroundColor =
(isUploadComplete || isUploadFailed) ? KashiColors.primaryColor : KashiColors.primary2;
final buttonTextColor = (isUploadComplete || isUploadFailed) ? KashiColors.whiteFA : KashiColors.primary04;
return Buttons.primary(
text: buttonText,
onPressed: buttonAction,
backgroundColor: buttonBackgroundColor,
textColor: buttonTextColor,
size: ButtonSize.md,
margin: EdgeInsets.zero,
);
}),
),
);
}
2
Answers
I got answer
}
Its working fine
https://github.com/salk52/Flutter-File-Upload-Download/blob/master/upload_download_app/lib/services/file_service.dart
Your implementation is time based instead of real progress based, so that is always going to look strange.
I suggest structuring you progress bar differently, as the sum of the progress in the two underlying activities (API calls). Define your progress
p = 0.7 * a + 0.3 * b
wherea
is the progress of API 1 andb
that of API 2, both going from 0 to 1. You show a progress bar based onp
and ideally your progressa
andb
is based on real upload progress (i.e.a = bytes uploaded / file size
for the file upload API).This way, both operations contribute to the overall progress, and the whole thing looks smooth and realistic. Note the values 0.7 and 0.3 simply represent what proportion of overall progress is provided by
a
andb
so you can change as you see fit, as long as they add to 1.0