I am getting a blue splash Animation problem when I am trying to swift between in the list of an image of the PageView
but when I am using simple Image then its working fine But when I am trying to get an image from SQLite Database then I am getting the blue Splash refresh Problem in animation
Working Animation with asset Image => https://i.imgur.com/2XJDpRx.mp4
Any help would be deeply appreciated!
Below is the SQLite Database
import 'dart:io';
import 'package:scoped_model/models/person.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper with ChangeNotifier {
static DatabaseHelper _databaseHelper;
static Database _database;
DatabaseHelper._namedConstructor();
static String _dbFileName = "animationWork.db";
static int _dbVersion = 1;
static String _personTableName = "person";
factory DatabaseHelper() {
// Singleton Design Structure - For multiple use of a single Object
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._namedConstructor();
return _databaseHelper;
} else {
return _databaseHelper;
}
}
Future<Database> _getDatabase() async {
// Singleton Design Structure - For multiple use of a single Object
if (_database == null) {
_database = await _initializeDatabase();
return _database;
} else {
return _database;
}
}
_initializeDatabase() async {
var databasePath = await getDatabasesPath();
var path = join(databasePath, _dbFileName);
bool exists = await databaseExists(path);
if (!exists) {
// exists == false
try {
print("Trying to create a new database cause it doesn\'t exist.");
await Directory(dirname(path)).create(recursive: true);
} catch (error) {
print('Database could not be created at path $path');
print("Error is : $error");
}
ByteData data = await rootBundle.load(join("assets", _dbFileName));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(path).writeAsBytes(bytes, flush: false);
} else {
print("Database exists.");
}
print(
"Opening database anyway, whether it was already there or has just been created.");
return await open database(path, version: _dbVersion, readOnly: false);
}
// GET "person" table from DB
// The method below returns the "person" table from the database.
// "person" table is a list of maps of Person objects
/*[
{"personID" : 1, "personDisplayName" : ""Meral", "personPhoneNumber" : "+905333654710", "personPhoto" : "AFH474JDFOWÊBB"},
{"personID" : 2, "personDisplayName" : ""Özge", "personPhoneNumber" : "+905325474445", "personPhoto" : "AFH474JDFOWÊBB"},
{"personID" : 3, "personDisplayName" : ""Semahat", "personPhoneNumber" : "+905325474445", "personPhoto" : "AFH474JDFOWÊBB"}
] */
Future<List<Map<String, dynamic>>> getPersonTable() async {
var db = await _getDatabase();
var result = db.query(_personTableName);
return result;
}
// CONVERT "person" table from DB to all Person list => List of Person objects
/*
The method below converts the "person" table - which consists of a list of maps of Person objects -
into a list of Person objects.
*/
Future<List<Person>> getAllPersonsList() async {
List<Map<String, dynamic>> listOfMapsOfPersonFromDatabase =
await getPersonTable();
var allPersonObjectsList = List<Person>();
var reversedList = List<Person>();
for (Map personMap in listOfMapsOfPersonFromDatabase) {
allPersonObjectsList.add(Person.fromMap(personMap));
}
if (allPersonObjectsList.length > 1) {
reversedList = List.from(allPersonObjectsList.reversed);
print("database getAllPersonList'te if uzunluk büyüktür 1'deyim");
for (var item in allPersonObjectsList) {
print(" ${item.personDisplayName}");
}
return reversedList;
} else {
print(
"database getAllPersonList'te else, uzunluk eşittir 1 ya da daha küçük'teyim");
print("allPersonObjectsList from db = $allPersonObjectsList");
return allPersonObjectsList;
}
}
// ADD Person to DB
Future<int> addPersonToPersonTableAtDb(Person person) async {
print(
"addPersonToPersonTableAtDb bloğundayım. Gelen personDisplayName = ${person.personDisplayName}");
var db = await _getDatabase();
var result = await db.insert(_personTableName, person.personToMap());
notifyListeners();
return result;
}
// UPDATE Person at DB
Future<int> updatePersonAtDb(Person personToUpdate) async {
var db = await _getDatabase();
var result = await db.update(_personTableName, personToUpdate.personToMap(),
where: "personID = ?", whereArgs: [personToUpdate.personID]);
notifyListeners();
return result;
}
// DELETE Person from DB
Future<int> deletePersonFromDb(int personID) async {
var db = await _getDatabase();
var result = db
.delete(_personTableName, where: "personID = ?", whereArgs: [personID]);
notifyListeners();
return result;
}
}
below is the Animation Screen Code
import 'dart:io';
import 'package:scoped_model/database/database_helper.dart';
import 'package:scoped_model/design_elements/size_config.dart';
import 'package:scoped_model/design_elements/smart_design.dart';
import 'package:scoped_model/models/person.dart';
import 'package:scoped_model/utils/format_photo.dart';
import 'package:dashed_circle/dashed_circle.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
//there is some way to improve the way we have to do this skvkjsdvjdjsjvdsjhljshdljfhjdsnvjzvjzjvnjsljdvljsdlvnsdv
import 'add_contact_screen.dart';
class AnimationScreen extends StatefulWidget {
List<Person> allPersonsFromDB;
AnimationScreen({this.allPersonsFromDB});
_AnimationScreenState createState() => _AnimationScreenState();
}
class _AnimationScreenState extends State<AnimationScreen>
with TickerProviderStateMixin {
//
int builtTime = 0;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _textEditingController = TextEditingController();
PageController _pageController;
var heightAppBar = AppBar().preferredSize.height;
DatabaseHelper databaseHelper;
List<Person> allPersonsFromDBReordered;
List<Person> personsToPresent;
int pageIndex = 0;
int personIndex = 0;
Animation dGap;
Animation dBase;
Animation dReverse;
Animation<Color> dColorAnimation;
AnimationController dController;
#override
void initState() {
super.initState();
print("AppBar height = ${AppBar().preferredSize.height}");
databaseHelper = DatabaseHelper();
_pageController =
PageController(initialPage: pageIndex, viewportFraction: .51);
allPersonsFromDBReordered = List<Person>();
personsToPresent = List<Person>();
animateDashedCircle();
getPersonsList();
}
bool isInteger(num value) => value is int || value == value.roundToDouble();
#override
void dispose() {
dController.dispose();
_pageController.dispose();
super.dispose();
}
Future<void> getPersonsList() async {
print('getPersonList called');
databaseHelper.getAllPersonsList().then((value) {
if (value.length != 0) {
setState(() {
print("setState 1 çağırıldı.");
personsToPresent = value;
print("Length of personsToPresent = ${personsToPresent.length}");
});
} else {
print("database'deki personList uzunluğu sıfır");
setState(() {
personsToPresent = [];
});
print('database deki Person listesi boş.');
}
});
}
void animateDashedCircle() {
dController = AnimationController(
vsync: this, duration: Duration(milliseconds: 1200));
dBase = CurvedAnimation(parent: dController, curve: Curves.ease);
dReverse = Tween<double>(begin: 0.0, end: -1.0).animate(dBase);
dColorAnimation =
ColorTween(begin: Colors.deepOrange, end: Colors.deepPurple)
.animate(dController);
dGap = Tween<double>(begin: 3.0, end: 0.0).animate(dBase);
dController.forward();
}
#override
Widget build(BuildContext context) {
builtTime++;
SizeConfig().init(context);
print(
"Screen height = ${MediaQuery.of(context).size.height}, , $builtTime kez build edildi.");
return Scaffold(
key: _scaffoldKey,
appBar: PreferredSize(
preferredSize: Size.fromHeight(SizeConfig.blockSizeVertical * 7),
child: AppBar(
backgroundColor: Colors.blueAccent,
centerTitle: true,
actions: [
Row(
children: [
Padding(
padding:
EdgeInsets.all(SmartDesign.getPadding(context, 8) / 2),
child: GestureDetector(
child: Icon(
Icons.person_add,
color: Colors.white,
size: SmartDesign.getPadding(context, 16) * 1.25,
),
onTap: () {
print('Add new person tapped.');
/* BURAYI YAZDIN BIRAKTIN ******** */
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ContactListScreen()))
.then((value) {
getPersonsList();
setState(() {
pageIndex = 0;
_pageController.jumpToPage(pageIndex);
});
});
},
/* BURAYI YAZDIN BIRAKTIN ******* */
),
),
Padding(
padding:
EdgeInsets.all(SmartDesign.getPadding(context, 8) / 2),
child: GestureDetector(
child: Icon(
Icons.list,
color: Colors.white,
size: SmartDesign.getPadding(context, 24) * 1.25,
),
onTap: () {},
),
),
],
)
],
title: Text(
'DashedCircle',
textAlign: TextAlign.end,
style: TextStyle(color: Colors.white),
),
),
),
body: Center(
child: Container(
color: Colors.white,
child: PageView.builder(
// clipBehavior: Clip.none,
itemCount:
personsToPresent.length <= 0 ? 1 : personsToPresent.length,
physics: BouncingScrollPhysics(),
controller: _pageController,
onPageChanged: (page) {
if (isInteger(page)) {
print("integer page index = $page");
animateDashedCircle();
setState(() {
pageIndex = page;
});
}
print('PageIndex : $page');
},
itemBuilder: (context, index) {
return Container(
child: Padding(
padding: EdgeInsets.only(
top: SmartDesign.getPadding(context, 16),
left: SmartDesign.getPadding(context, 6),
right: SmartDesign.getPadding(context, 6)),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
index == pageIndex
? Container(
child: AnimatedBuilder(
animation: dController,
builder: (_, child) {
return RotationTransition(
turns: dReverse,
child: DashedCircle(
gapSize: dGap.value,
dashes: 50,
color: dColorAnimation.value,
child: AnimatedBuilder(
animation: dController,
builder: (_, child) {
return RotationTransition(
turns: dBase,
child: Padding(
padding: EdgeInsets.all(
(SmartDesign.getPadding(
context, 6) /
2)),
child: personsToPresent.length <=
0
? CircleAvatar(
//radius: 90,
radius:
MediaQuery.of(context)
.size
.width /
7,
backgroundImage: AssetImage(
"assets/images/blueAvatar2_Half.png"))
: personsToPresent[index]
.personPhoto ==
null
? CircleAvatar(
//radius: 90,
radius: MediaQuery.of(
context)
.size
.width /
7,
backgroundImage:
AssetImage(
"assets/images/blue_avatar.png"),
/*child: Center(
child: Text(
'Initials')),*/
)
: CircleAvatar(
//radius: 90,
radius: MediaQuery.of(
context)
.size
.width /
7,
backgroundImage: FormatPhoto
.memoryImageFromBase64String(
personsToPresent[
pageIndex]
.personPhoto),
),
),
);
},
child: Placeholder(
key: Key('colorAnimated')),
),
),
);
},
child: Placeholder(key: Key('animated')),
),
//color: Colors.red,
)
: personsToPresent[index].personPhoto == null
? Container(
child: Padding(
padding: EdgeInsets.all(
(SmartDesign.getPadding(context, 6) /
2)),
child: CircleAvatar(
//radius: 90,
radius:
MediaQuery.of(context).size.width / 7,
backgroundImage: AssetImage(
"assets/images/green_avatar.png"),
),
),
)
: Container(
child: Padding(
padding: EdgeInsets.all(
(SmartDesign.getPadding(context, 6) /
2)),
child: CircleAvatar(
//radius: 90,
radius:
MediaQuery.of(context).size.width /
7,
backgroundImage: FormatPhoto
.memoryImageFromBase64String(
personsToPresent[index]
.personPhoto)),
),
),
Padding(
padding: EdgeInsets.only(
top: (SmartDesign.getPadding(context, 8)),
left: (SmartDesign.getPadding(context, 6) / 1.5),
right: (SmartDesign.getPadding(context, 6) / 1.5),
),
child: Column(
children: [
Text(
personsToPresent.length <= 0
? ""
: "${personsToPresent[index].personDisplayName}",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize:
MediaQuery.of(context).size.width / 23,
color: Colors
.black // Font size iPhone 11'e göre 18 bulmak için
),
textAlign: TextAlign.center,
),
],
),
),
//_buildCard(index),
],
),
),
);
}, // itemBuilder(){}
),
),
),
);
}
}
I added a Video file to Show the error problem => https://i.imgur.com/ilrURiW.mp4
Every time your AnimationScreen builds it keeps on reloading the image with FormatPhoto.memoryImageFromBase64String(...).
To fix this, when you load your users in getPersonsList and when you are creating your Person objects, you should call the function FormatPhoto.memoryImageFromBase64String(...) and assign it to personPhoto or a new variable. Then use that as the image like this:
CircleAvatar(
radius: MediaQuery.of(context).size.width / 7,
// or personsToPresent[pageIndex].photoThatWasLoaded
backgroundImage: personsToPresent[pageIndex].personPhoto,
)
Related
can video player play image for 10sec then loop to next video ?
How can i loop image only for 10sec then loop into next new video in my video clip data or video player controller ?
I manage to add video to video player but don't know how to add image to the player.
video clip data
class VideoClip {
final String fileName;
final String thumbName;
final String title;
final String parent;
int runningTime;
VideoClip(this.title, this.fileName, this.thumbName, this.runningTime, this.parent);
String videoPath() {
return "$parent/$fileName";
}
String thumbPath() {
return "$parent/$thumbName";
}
static List<VideoClip> remoteClips = [
VideoClip("For Bigger Fun", "ForBiggerFun.mp4", "images/ForBiggerFun.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
VideoClip("Elephant Dream", "ElephantsDream.mp4", "images/ForBiggerBlazes.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
VideoClip("BigBuckBunny", "BigBuckBunny.mp4", "images/BigBuckBunny.jpg", 0, "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample"),
];
}
Video player controller
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_video_list_sample/clips.dart';
import 'package:video_player/video_player.dart';
import 'package:wakelock/wakelock.dart';
class PlayPage extends StatefulWidget {
PlayPage({Key key, #required this.clips}) : super(key: key);
final List<VideoClip> clips;
#override
_PlayPageState createState() => _PlayPageState();
}
class _PlayPageState extends State<PlayPage> {
VideoPlayerController _controller;
List<VideoClip> get _clips {
return widget.clips;
}
var _playingIndex = -1;
var _disposed = false;
var _isFullScreen = false;
var _isEndOfClip = false;
var _progress = 0.0;
var _showingDialog = false;
Timer _timerVisibleControl;
double _controlAlpha = 1.0;
var _playing = false;
bool get _isPlaying {
return _playing;
}
set _isPlaying(bool value) {
_playing = value;
_timerVisibleControl?.cancel();
if (value) {
_timerVisibleControl = Timer(Duration(seconds: 2), () {
if (_disposed) return;
setState(() {
_controlAlpha = 0.0;
});
});
} else {
_timerVisibleControl = Timer(Duration(milliseconds: 200), () {
if (_disposed) return;
setState(() {
_controlAlpha = 1.0;
});
});
}
}
void _onTapVideo() {
debugPrint("_onTapVideo $_controlAlpha");
setState(() {
_controlAlpha = _controlAlpha > 0 ? 0 : 1;
});
_timerVisibleControl?.cancel();
_timerVisibleControl = Timer(Duration(seconds: 2), () {
if (_isPlaying) {
setState(() {
_controlAlpha = 0.0;
});
}
});
}
#override
void initState() {
Wakelock.enable();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
_initializeAndPlay(0);
super.initState();
}
#override
void dispose() {
_disposed = true;
_timerVisibleControl?.cancel();
Wakelock.disable();
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
_exitFullScreen();
_controller?.pause(); // mute instantly
_controller?.dispose();
_controller = null;
super.dispose();
}
void _toggleFullscreen() async {
if (_isFullScreen) {
_exitFullScreen();
} else {
_enterFullScreen();
}
}
void _enterFullScreen() async {
debugPrint("enterFullScreen");
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
if (_disposed) return;
setState(() {
_isFullScreen = true;
});
}
void _exitFullScreen() async {
debugPrint("exitFullScreen");
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
if (_disposed) return;
setState(() {
_isFullScreen = false;
});
}
void _initializeAndPlay(int index) async {
print("_initializeAndPlay ---------> $index");
final clip = _clips[index];
final controller = clip.parent.startsWith("http")
? VideoPlayerController.network(clip.videoPath())
: VideoPlayerController.asset(clip.videoPath());
final old = _controller;
_controller = controller;
if (old != null) {
old.removeListener(_onControllerUpdated);
old.pause();
debugPrint("---- old contoller paused.");
}
debugPrint("---- controller changed.");
setState(() {});
controller
..initialize().then((_) {
debugPrint("---- controller initialized");
old?.dispose();
_playingIndex = index;
_duration = null;
_position = null;
controller.addListener(_onControllerUpdated);
controller.play();
setState(() {});
});
}
var _updateProgressInterval = 0.0;
Duration _duration;
Duration _position;
void _onControllerUpdated() async {
if (_disposed) return;
// blocking too many updation
// important !!
final now = DateTime.now().millisecondsSinceEpoch;
if (_updateProgressInterval > now) {
return;
}
_updateProgressInterval = now + 500.0;
final controller = _controller;
if (controller == null) return;
if (!controller.value.isInitialized) return;
if (_duration == null) {
_duration = _controller.value.duration;
}
var duration = _duration;
if (duration == null) return;
var position = await controller.position;
_position = position;
final playing = controller.value.isPlaying;
final isEndOfClip = position.inMilliseconds > 0 && position.inSeconds + 1 >= duration.inSeconds;
if (playing) {
// handle progress indicator
if (_disposed) return;
setState(() {
_progress = position.inMilliseconds.ceilToDouble() / duration.inMilliseconds.ceilToDouble();
});
}
// handle clip end
if (_isPlaying != playing || _isEndOfClip != isEndOfClip) {
_isPlaying = playing;
_isEndOfClip = isEndOfClip;
debugPrint("updated -----> isPlaying=$playing / isEndOfClip=$isEndOfClip");
if (isEndOfClip && !playing) {
debugPrint("========================== End of Clip / Handle NEXT ========================== ");
final isComplete = _playingIndex == _clips.length - 1;
if (isComplete) {
print("played all!!");
if (!_showingDialog) {
_showingDialog = true;
_showPlayedAllDialog().then((value) {
_exitFullScreen();
_showingDialog = false;
});
}
} else {
_initializeAndPlay(_playingIndex + 1);
}
}
}
}
Future<bool> _showPlayedAllDialog() async {
return showDialog<bool>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(child: Text("Played all videos.")),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, true),
child: Text("Close"),
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _isFullScreen
? null
: AppBar(
title: Text("Play View"),
),
body: _isFullScreen
? Container(
child: Center(child: _playView(context)),
decoration: BoxDecoration(color: Colors.black),
)
: Column(children: <Widget>[
Container(
child: Center(child: _playView(context)),
decoration: BoxDecoration(color: Colors.black),
),
Expanded(
child: _listView(),
),
]),
);
}
void _onTapCard(int index) {
_initializeAndPlay(index);
}
Widget _playView(BuildContext context) {
final controller = _controller;
if (controller != null && controller.value.isInitialized) {
return AspectRatio(
//aspectRatio: controller.value.aspectRatio,
aspectRatio: 16.0 / 9.0,
child: Stack(
children: <Widget>[
GestureDetector(
child: VideoPlayer(controller),
onTap: _onTapVideo,
),
_controlAlpha > 0
? AnimatedOpacity(
opacity: _controlAlpha,
duration: Duration(milliseconds: 250),
child: _controlView(context),
)
: Container(),
],
),
);
} else {
return AspectRatio(
aspectRatio: 16.0 / 9.0,
child: Center(
child: Text(
"Preparing ...",
style: TextStyle(color: Colors.white70, fontWeight: FontWeight.bold, fontSize: 18.0),
)),
);
}
}
Widget _listView() {
return ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
itemCount: _clips.length,
itemBuilder: (BuildContext context, int index) {
return InkWell(
borderRadius: BorderRadius.all(Radius.circular(6)),
splashColor: Colors.blue[100],
onTap: () {
_onTapCard(index);
},
child: _buildCard(index),
);
},
).build(context);
}
Widget _controlView(BuildContext context) {
return Column(
children: <Widget>[
_topUI(),
Expanded(
child: _centerUI(),
),
_bottomUI()
],
);
}
Widget _centerUI() {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(
onPressed: () async {
final index = _playingIndex - 1;
if (index > 0 && _clips.length > 0) {
_initializeAndPlay(index);
}
},
child: Icon(
Icons.fast_rewind,
size: 36.0,
color: Colors.white,
),
),
TextButton(
onPressed: () async {
if (_isPlaying) {
_controller?.pause();
_isPlaying = false;
} else {
final controller = _controller;
if (controller != null) {
final pos = _position?.inSeconds ?? 0;
final dur = _duration?.inSeconds ?? 0;
final isEnd = pos == dur;
if (isEnd) {
_initializeAndPlay(_playingIndex);
} else {
controller.play();
}
}
}
setState(() {});
},
child: Icon(
_isPlaying ? Icons.pause : Icons.play_arrow,
size: 56.0,
color: Colors.white,
),
),
TextButton(
onPressed: () async {
final index = _playingIndex + 1;
if (index < _clips.length - 1) {
_initializeAndPlay(index);
}
},
child: Icon(
Icons.fast_forward,
size: 36.0,
color: Colors.white,
),
),
],
));
}
String convertTwo(int value) {
return value < 10 ? "0$value" : "$value";
}
Widget _topUI() {
final noMute = (_controller?.value?.volume ?? 0) > 0;
final duration = _duration?.inSeconds ?? 0;
final head = _position?.inSeconds ?? 0;
final remained = max(0, duration - head);
final min = convertTwo(remained ~/ 60.0);
final sec = convertTwo(remained % 60);
return Row(
children: <Widget>[
InkWell(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, boxShadow: [
BoxShadow(offset: const Offset(0.0, 0.0), blurRadius: 4.0, color: Color.fromARGB(50, 0, 0, 0)),
]),
child: Icon(
noMute ? Icons.volume_up : Icons.volume_off,
color: Colors.white,
)),
),
onTap: () {
if (noMute) {
_controller?.setVolume(0);
} else {
_controller?.setVolume(1.0);
}
setState(() {});
},
),
Expanded(
child: Container(),
),
Text(
"$min:$sec",
style: TextStyle(
color: Colors.white,
shadows: <Shadow>[
Shadow(
offset: Offset(0.0, 1.0),
blurRadius: 4.0,
color: Color.fromARGB(150, 0, 0, 0),
),
],
),
),
SizedBox(width: 10)
],
);
}
Widget _bottomUI() {
return Row(
children: <Widget>[
SizedBox(width: 20),
Expanded(
child: Slider(
value: max(0, min(_progress * 100, 100)),
min: 0,
max: 100,
onChanged: (value) {
setState(() {
_progress = value * 0.01;
});
},
onChangeStart: (value) {
debugPrint("-- onChangeStart $value");
_controller?.pause();
},
onChangeEnd: (value) {
debugPrint("-- onChangeEnd $value");
final duration = _controller?.value?.duration;
if (duration != null) {
var newValue = max(0, min(value, 99)) * 0.01;
var millis = (duration.inMilliseconds * newValue).toInt();
_controller?.seekTo(Duration(milliseconds: millis));
_controller?.play();
}
},
),
),
IconButton(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
color: Colors.yellow,
icon: Icon(
Icons.fullscreen,
color: Colors.white,
),
onPressed: _toggleFullscreen,
),
],
);
}
Widget _buildCard(int index) {
final clip = _clips[index];
final playing = index == _playingIndex;
String runtime;
if (clip.runningTime > 60) {
runtime = "${clip.runningTime ~/ 60}분 ${clip.runningTime % 60}초";
} else {
runtime = "${clip.runningTime % 60}초";
}
return Card(
child: Container(
padding: EdgeInsets.all(4),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 8),
child: clip.parent.startsWith("http")
? Image.network(clip.thumbPath(), width: 70, height: 50, fit: BoxFit.fill)
: Image.asset(clip.thumbPath(), width: 70, height: 50, fit: BoxFit.fill),
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(clip.title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
Padding(
child: Text("$runtime", style: TextStyle(color: Colors.grey[500])),
padding: EdgeInsets.only(top: 3),
)
]),
),
Padding(
padding: EdgeInsets.all(8.0),
child: playing
? Icon(Icons.play_arrow)
: Icon(
Icons.play_arrow,
color: Colors.grey.shade300,
),
),
],
),
),
);
}
}
hello guys!
i am using flutter story view package to implement story functionality in my app and i am using camera plugin to record video and upload, however the content type of the uploaded video is application/octet-stream when i receive it on my server and only when it is sent from ios. for android everything is fine and i get the content type as video/mp4.
can you guys please help me.
i have not done anything fancy i have just used the basic functionality of start recording and stop recording of the camra plugin.
// ignore_for_file: use_build_context_synchronously
List<CameraDescription>? cameras;
class CameraScreen extends StatefulWidget {
const CameraScreen({Key? key}) : super(key: key);
#override
State<CameraScreen> createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
CameraController? _cameraController;
Future<void>? cameraValue;
bool flash = false;
bool iscamerafront = true;
static const _maxSeconds = 30;
ValueNotifier<bool> visible = ValueNotifier(false);
ValueNotifier<bool> isRecoring = ValueNotifier(false);
TimerProvider? _timerProvider;
#override
void initState() {
super.initState();
_timerProvider = Provider.of(context, listen: false);
_cameraController = CameraController(cameras![0], ResolutionPreset.veryHigh,
imageFormatGroup: ImageFormatGroup.bgra8888);
cameraValue = _cameraController?.initialize();
_cameraController?.prepareForVideoRecording();
lockCamera();
}
lockCamera() async {
await _cameraController!.lockCaptureOrientation();
}
#override
void dispose() {
super.dispose();
visible.dispose();
isRecoring.dispose();
_cameraController?.dispose();
}
#override
Widget build(BuildContext context) {
print("camera_screen");
final size = MediaQuery.of(context).size;
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
centerTitle: true,
title: ValueListenableBuilder<bool>(
valueListenable: visible,
builder: (context, value, child) {
return Visibility(
visible: value,
child: Container(
padding: const EdgeInsets.all(10),
width: 50,
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: Center(
child: Consumer<TimerProvider>(
builder: (context, value, child) {
if (value.seconds == 0) {
if (_cameraController!.value.isRecordingVideo) {
stopRecording();
}
}
return Text(
value.seconds.toString(),
style: CustomStyles.fixAppBarTextStyle
.copyWith(color: Colors.white),
);
}),
),
));
}),
backgroundColor: Colors.transparent,
leadingWidth: 100,
leading: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Cancel",
style: CustomStyles.fixMediumBodyTextStyle
.copyWith(color: Colors.white),
),
)),
),
),
body: SafeArea(
top: false,
child: Stack(
children: [
FutureBuilder(
future: cameraValue,
builder: (context, snapshot) {
_cameraController?.lockCaptureOrientation();
if (snapshot.connectionState == ConnectionState.done) {
return Transform.scale(
scale: size.aspectRatio *
_cameraController!.value.aspectRatio <
1
? 1 /
(size.aspectRatio *
_cameraController!.value.aspectRatio)
: size.aspectRatio *
_cameraController!.value.aspectRatio,
child: Center(child: CameraPreview(_cameraController!)),
);
} else {
return const Center(
child: CircularProgressIndicator.adaptive(),
);
}
}),
Positioned(
bottom: 0.0,
child: Container(
padding: const EdgeInsets.only(top: 5, bottom: 5),
width: Dimensions.screenWidth,
child: Column(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: Center(
child: IconButton(
icon: Icon(
flash ? Icons.flash_on : Icons.flash_off,
color: Colors.white,
size: 28,
),
onPressed: () {
setState(() {
flash = !flash;
});
flash
? _cameraController!
.setFlashMode(FlashMode.torch)
: _cameraController!
.setFlashMode(FlashMode.off);
}),
),
),
ValueListenableBuilder<bool>(
valueListenable: isRecoring,
builder: (context, value, child) {
return GestureDetector(
onLongPress: () {
startRecording();
},
onLongPressUp: () {
stopRecording();
},
onTap: () {
if (value == false) {
takePhoto(context);
}
},
child: value == true
? const CircleAvatar(
radius: 50,
backgroundColor: Colors.white30,
child: CircleAvatar(
radius: 35,
backgroundColor: Colors.red,
),
)
: const CircleAvatar(
radius: 35,
backgroundColor: Colors.white30,
child: CircleAvatar(
radius: 25,
backgroundColor: Colors.white,
),
));
}),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.5)),
child: IconButton(
icon: const Icon(
Icons.flip_camera_ios,
color: Colors.white,
size: 28,
),
onPressed: () async {
setState(() {
iscamerafront = !iscamerafront;
});
int cameraPos = iscamerafront ? 0 : 1;
_cameraController = CameraController(
cameras![cameraPos], ResolutionPreset.high);
cameraValue = _cameraController?.initialize();
}),
),
],
),
addVerticalSpace(6),
const Text(
"Hold for Video, tap for photo",
style: TextStyle(
color: Colors.white,
),
textAlign: TextAlign.center,
),
addVerticalSpace(5),
],
),
),
),
],
),
),
);
}
void startRecording() async {
visible.value = true;
_timerProvider?.startCountDown(_maxSeconds);
isRecoring.value = true;
await _cameraController?.startVideoRecording();
}
void stopRecording() async {
XFile videopath = await _cameraController!.stopVideoRecording();
_timerProvider?.stopTimer();
isRecoring.value = false;
Navigator.push(
context,
MaterialPageRoute(
builder: (builder) => CameraResult(
image: File(videopath.path),
isImage: false,
))).then((value) {
visible.value = false;
});
}
void takePhoto(BuildContext context) async {
XFile file = await _cameraController!.takePicture();
SystemSound.play(SystemSoundType.click);
final img.Image? capturedImage =
img.decodeImage(await File(file.path).readAsBytes());
final img.Image orientedImage = img.flipHorizontal(capturedImage!);
File finalImage =
await File(file.path).writeAsBytes(img.encodeJpg(orientedImage));
Navigator.push(
context,
CupertinoPageRoute(
builder: (builder) => CameraResult(
image: finalImage,
isImage: true,
))).then((value) {
visible.value = false;
});
}
}
the upload function is as bellow
{File? shouts,
File? thumbnail,
int? duration,
bool? isImage,
required String userId,
required OnUploadProgressCallback onUploadProgress}) async {
assert(shouts != null);
try {
var url = Constants.POSTSHOUTURL;
final httpClient = FileService.getHttpClient();
final request = await httpClient.postUrl(Uri.parse(url));
int byteCount = 0;
var multipart;
if (isImage == false) {
multipart = await http.MultipartFile.fromPath(
'story-media', shouts!.path,
contentType: MediaType("video", "mp4"));
} else {
multipart = await http.MultipartFile.fromPath(
'story-media',
shouts!.path,
);
}
var multipart2 =
await http.MultipartFile.fromPath('thumbnail', thumbnail!.path);
var requestMultipart = http.MultipartRequest('POST', Uri.parse(url));
requestMultipart.fields["userid"] = userId.toString();
requestMultipart.fields["duration"] = duration.toString();
requestMultipart.headers['Content-type'] = 'multipart/form-data';
requestMultipart.files.add(multipart);
requestMultipart.files.add(multipart2);
var msStream = requestMultipart.finalize();
var totalByteLength = requestMultipart.contentLength;
request.contentLength = totalByteLength;
request.headers.add(HttpHeaders.authorizationHeader,
"Bearer ${Hive.box<UserData>(Constants.userDb).get(Hive.box<UserData>(Constants.userDb).keyAt(0))!.token}");
request.headers.set(HttpHeaders.contentTypeHeader,
requestMultipart.headers[HttpHeaders.contentTypeHeader]!);
Stream<List<int>> streamUpload = msStream.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
sink.add(data);
byteCount += data.length;
if (onUploadProgress != null) {
onUploadProgress(byteCount, totalByteLength);
// CALL STATUS CALLBACK;
}
},
handleError: (error, stack, sink) {
throw error;
},
handleDone: (sink) {
sink.close();
// UPLOAD DONE;
},
),
);
await request.addStream(streamUpload);
final httpResponse = await request.close();
var statusCode = httpResponse.statusCode;
if (statusCode ~/ 100 == 2) {
onUploadProgress(0, 0);
return await FileService.readResponseAsString(httpResponse);
}
return "";
} on SocketException {
throw FetchDataException("No internet to upload Shout!");
}
}```
i have used necessary info.plist setting also
I am having an issue with my favorite button. I have connected it with real-time firebase and each time I press the favorite icon it changes the state in firebase either true or false. when I press the heart button it always shows me different output sometimes it becomes red or black without affecting the other and sometimes it changes the other ones too (red or black). I have tried a lot to solve this but I am unable to get a solution for it.
class Home_page extends StatefulWidget {
#override
State<Home_page> createState() => _Home_pageState();
}
class _Home_pageState extends State<Home_page> {
//late StreamSubscription _dailySpecialStream;
var clr = Colors.grey;
//var item;
final List<String> _listItem = [
'assets/audi3.png',
'assets/audi.png',
'assets/audi2.png',
'assets/audi.png',
'assets/audi2.png',
'assets/audi3.png',
];
var name;
var auth1 = FirebaseAuth.instance.currentUser;
Data? c;
late final Value;
List<Data> dataList = [];
List<dynamic> d = [];
List<dynamic> favList = [];
#override
void initState() {
print("object");
super.initState();
print(auth1);
_activateListener();
}
void _activateListener() {
final _database = FirebaseDatabase.instance.ref();
_database.child('user').get().then((snapshot) {
dataList.clear();
favList.clear();
print("hello");
Value = snapshot.value as Map;
final datas = snapshot.children.forEach((element) {
d.add(element.key);
});
for (var k in d) {
//print("");
print(Value[k]['imgUrl']);
print((Value[k]['carName']).runtimeType);
dataList.add(Data(
Value[k]['carName'],
Value[k]['price'],
Value[k]["imgUrl"],
k,
));
if (auth1 != null) {
print(k);
print("auth1");
print(auth1);
DatabaseReference reference = FirebaseDatabase.instance
.ref()
.child("user")
.child(k)
.child("fav")
.child(auth1!.uid)
.child("state");
reference.get().then((s) {
print(s.value);
print("upp is vla");
if (s.value != null) {
if (s.value == true) {
// print(s.value);
// print("true");
favList.add(true);
print(favList);
} else {
//print(s.value);
print("false1");
favList.add(false);
//print(favList);
}
} else {
print("inelse");
favList.add(false);
print(favList);
}
});
}
}
Timer(Duration(seconds: 1), () {
setState(() {
inspect(favList);
});
});
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Container(
color: Colors.white,
child: Stack(
children: [
Image.asset(
"assets/top.png",
fit: BoxFit.cover,
),
Column(
children: [
SizedBox(
height: 30,
),
search(context),
SizedBox(
height: 80,
),
Expanded(
child: GridView.count(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 40,
children: List.generate(
dataList.length, (index) => GridDesign(index)).toList(),
)),
],
)
],
),
),
),
);
}
SizedBox GridDesign(int index) {
return SizedBox(
child: Column(
children: [
Stack(
clipBehavior: Clip.none,
children: [
Container(
height: 15.h,
width: 45.w,
margin: EdgeInsets.only(left: 12, top: 12, right: 16),
decoration: BoxDecoration(
// border: Border.all(color: Colors.grey,style: BorderStyle.solid,width: 2),
borderRadius: BorderRadius.circular(5),
image: DecorationImage(
image: NetworkImage(dataList[index].url.toString()),
fit: BoxFit.cover)),
),
Positioned(
bottom: -3,
right: 5,
child: Container(
height: 32,
width: 32,
child: Neumorphic(
padding: EdgeInsets.zero,
style: NeumorphicStyle(
//shape: NeumorphicShape.concave,
boxShape: NeumorphicBoxShape.roundRect(
BorderRadius.circular(50)),
color: Colors.white),
child: favList[index]
? IconButton(
padding: EdgeInsets.zero,
onPressed: () {
if (auth1 != null) {
print(auth1);
print("it's not null");
DatabaseReference favRef = FirebaseDatabase
.instance
.ref()
.child("user")
.child(
dataList[index].uploadId.toString())
.child("fav")
.child(auth1!.uid)
.child("state");
favRef.set(false);
setState(() {
favFun();
});
}
print("object");
},
icon: Icon(
Icons.favorite,
color: Colors.red,
),
)
: IconButton(
padding: EdgeInsets.zero,
onPressed: () {
if (auth1 != null) {
print("it's not null1");
print(auth1);
DatabaseReference favRef = FirebaseDatabase
.instance
.ref()
.child("user")
.child(
dataList[index].uploadId.toString())
.child("fav")
.child(auth1!.uid)
.child("state");
favRef.set(true);
setState(() {
favFun();
});
}
print("object");
},
icon: Icon(
Icons.favorite,
))),
),
),
],
),
SizedBox(
height: 4,
),
Expanded(child: Text(dataList[index].CarName.toString())),
SizedBox(height: 2),
Expanded(
child: Text("R" + dataList[index].price.toString()),
),
SizedBox(height: 3),
Expanded(
child: ElevatedButton(
onPressed: () =>
Get.to(DetailPage(dataList[index].Url, dataList[index].Price)),
child: Text("Details"),
style: ElevatedButton.styleFrom(primary: myColor),
))
],
),
);
}
void favFun() {
final _database = FirebaseDatabase.instance.ref();
_database.child('user').get().then((snapshot) {
favList.clear();
for (var k in d) {
//print("");
if (auth1 != null) {
print(k);
print("auth1");
print(auth1);
DatabaseReference reference = FirebaseDatabase.instance
.ref()
.child("user")
.child(k)
.child("fav")
.child(auth1!.uid)
.child("state");
reference.once().then((DatabaseEvent s) {
print(s.snapshot.value);
print("upp is vla");
if (s.snapshot.value != null) {
if (s.snapshot.value == true) {
// print(s.value);
// print("true");
favList.add(true);
print(favList);
} else {
//print(s.value);
print("false1");
favList.add(false);
//print(favList);
}
} else {
print("inelse");
favList.add(false);
print(favList);
}
});
}
}
Timer(Duration(seconds: 1), () {
setState(() {
//inspect(favList);
});
});
});
}
}
Have a look at the picture to get a better view
I uses Listview.builder. it detect scroll end to many time that is why API call to many times and add duplicate Data in Listview.
Code:-
ListView.builder(
controller: _scrollController
..addListener(() async {
if (_scrollController
.position.pixels -
10 ==
_scrollController.position
.maxScrollExtent -
10 &&
!state.isPaginationLoading) {
print("Scroll End TEst Screen");
await ctx
.read<ProfileCubit>()
.getProfiles(
context, true, null);
}
Dont put logic code inside build. In your case _scrollController will addListener every times widget build called, cause multiple handle will trigger.
Advice for you is create and put handle logic to a function, put addListener/removeListener in initState/dispose because they was called only once.
With your problem, you can create a variale to check api was called yet and prevent other call.
class AppState extends State<App> {
var scroll = ScrollController();
var preventCall = false;
#override
initState() {
scroll.addListener(onScroll);
super.initState();
}
#override
void dispose() {
scroll.removeListener(onScroll);
super.dispose();
}
Future yourFuture() async {}
void onScroll() {
var position = scroll.position.pixels;
if (position >= scroll.position.maxScrollExtent - 10) {
if (!preventCall) {
yourFuture().then((_) => preventCall = false);
preventCall = true;
}
}
}
#override
Widget build(BuildContext context) {
return ...
}
}
You can add a condition to check if API call is happening or not and based on it you can you can call the API. You would also need to handle pagination logic if all info is loaded.
you can always check if you reached at the limit of max Limit of your scroll controller then you can call API
condition is like
child: ListView.builder(
controller: _controller
..addListener(() async {
if (_controller.position.pixels >
_controller.position.maxScrollExtent) {
_loadMore();
}
}),
you may be like full example
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
class Paggination2HomeScreen extends StatefulWidget {
const Paggination2HomeScreen({Key? key}) : super(key: key);
#override
State<Paggination2HomeScreen> createState() => _Paggination2HomeScreenState();
}
class _Paggination2HomeScreenState extends State<Paggination2HomeScreen> {
final String _baseUrl = "https://jsonplaceholder.typicode.com/photos";
int _page = 1;
final int _limit = 10;
bool _hasNextPage = true;
bool _isFirstLoadRunning = false;
bool _isLoadMoreRunning = false;
List _posts = [];
void _firstLoad() async {
setState(() {
_isFirstLoadRunning = true;
});
try {
final res = await http.get(
Uri.parse("$_baseUrl?_page=$_page&_limit=$_limit"),
);
if (res.statusCode == 200) {
setState(() {
_posts = jsonDecode(res.body);
});
log('receivedData : ${jsonDecode(res.body)}');
}
} catch (e) {
if (kDebugMode) {
log('Something went wrong');
}
}
setState(() {
_isFirstLoadRunning = false;
});
}
void _loadMore() async {
if (_hasNextPage == true &&
_isFirstLoadRunning == false &&
_isLoadMoreRunning == false) {
setState(() {
_isLoadMoreRunning = true;
});
_page++;
try {
final res = await http.get(
Uri.parse('$_baseUrl?_page=$_page&_limit=$_limit'),
);
log('url : $_baseUrl?_page=$_page&_limit=$_limit');
if (res.statusCode == 200) {
final List fetchedPost = jsonDecode(res.body);
if (fetchedPost.isNotEmpty) {
setState(() {
_posts.addAll(fetchedPost);
});
} else {
setState(() {
_hasNextPage = false;
});
}
}
} catch (e) {
if (kDebugMode) {
log('something went wrong');
}
}
setState(() {
_isLoadMoreRunning = false;
});
}
}
// the controller for listyView
late ScrollController _controller;
#override
void initState() {
super.initState();
_firstLoad();
_controller = ScrollController();
}
#override
void dispose() {
super.dispose();
_controller.removeListener(_loadMore);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.orangeAccent,
elevation: 0.0,
centerTitle: true,
title: Text(
'Paggination',
style: TextStyle(
color: Colors.black.withOpacity(0.52),
fontSize: 20,
),
),
systemOverlayStyle: const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
),
body: _isFirstLoadRunning
? Center(
child: CircularProgressIndicator(
color: Colors.black.withOpacity(0.25),
),
)
: Column(
children: [
Expanded(
child: ListView.builder(
controller: _controller
..addListener(() async {
if (_controller.position.pixels >
_controller.position.maxScrollExtent) {
_loadMore();
}
}),
physics: const BouncingScrollPhysics(),
itemCount: _posts.length,
itemBuilder: (context, index) => Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.065),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: Colors.black.withOpacity(0.2),
width: 0.5,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'ID : ${_posts.elementAt(index)['id']}',
),
Text(
'AlbumID : ${_posts.elementAt(index)['albumId']}',
),
Text(
'Title : ${_posts.elementAt(index)['title']}',
textAlign: TextAlign.justify,
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: MediaQuery.of(context).size.width * 0.4,
width: MediaQuery.of(context).size.width * 0.4,
child: Image.network(
_posts.elementAt(index)['url'],
),
),
SizedBox(
height: MediaQuery.of(context).size.width * 0.4,
width: MediaQuery.of(context).size.width * 0.4,
child: Image.network(
_posts.elementAt(index)['thumbnailUrl'],
),
),
],
),
],
),
),
),
),
// when the _loadMore running
if (_isLoadMoreRunning == true)
Container(
color: Colors.transparent,
padding: const EdgeInsets.only(top: 10, bottom: 20),
child: const Center(
child: CircularProgressIndicator(
color: Colors.amberAccent,
strokeWidth: 3,
backgroundColor: Colors.transparent,
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
),
),
),
if (_hasNextPage == false)
SafeArea(
child: Container(
width: double.maxFinite,
padding: const EdgeInsets.only(top: 20, bottom: 20),
color: Colors.orangeAccent,
child: const Text('you get all'),
),
)
],
),
);
}
}
After the addition of this plugin (https://pub.dev/packages/carp_background_location), all my Streambuilders with Firestore stopped working. It returns null for all snapshot.data of my app.
Situation:
StreamBuilder works for all collections from Firestore on the app
Use carp_background_location like on the example/lib/main.dart on a specific screen
All streams return null on snapshot.data on the app
Normal behaviour just comes back when I close the app and run main.dart again (and don't run background location).
This was the only plugin that worked for the case I needed with location (background), so changing the plugin would not be ideal. Actually I suspect that forcing the background work is causing this (maybe other plugins inside this plugin), but I am not able to find the answer. No error or warn is shown, what is making it more difficult.
Could someone help me on this please? Is there any function to get the "normal" behaviour after using the background plugin?
On this screen I am listening to Firestore with Streambuilder:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:provider/provider.dart';
import 'begin_activity.dart';
class MainApp extends StatefulWidget {
#override
_MainAppState createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
final _firestore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
void moveToPage() async {
await Navigator.push(
context,
MaterialPageRoute(builder: (context) => LocationStreamWidget()),
);
}
return Scaffold(
backgroundColor: Colors.grey,
floatingActionButton: FloatingActionButton(
mini: true,
onPressed: () {
moveToPage();
},
child: Icon(
Icons.add,
color: Colors.white,
size: 30,
),
backgroundColor: Colors.blue,
),
body: Stack(
children: [
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
child: StreamBuilder(
stream: _firestore
.collection('users')
.doc('user_id')
.collection('corridas')
.orderBy('date', descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.data == null) {
return Center(
child: SpinKitCubeGrid(
size: 51.0, color: Colors.blue,));
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.data.docs.isEmpty) {
return Container(
height: MediaQuery.of(context).size.height * 0.55,
child: Padding(
padding: const EdgeInsets.fromLTRB(8.0, 50.0, 8.0, 0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(
Icons.directions_run,
size: 50,
),
Text(
Strings.noTracks,
style: TextParameters.noActivities,
textAlign: TextAlign.center,
),
],
),
));
} else if (snapshot.hasData) {
return Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
final docAux = snapshot.data.docs;
return Container(
child: Column(
children: [
Text(_printDate(docAux[index]['date'])
.toString()),
Text(
docAux[index]['distance'].toStringAsFixed(2),
),
],
));
},
),
),
],
);
} else {
return Center(
child: SpinKitCubeGrid(
size: 51.0, color: Colors.blue,));
}
},
),
),
],
),
);
}
}
String _printTime(int timeInMilliseconds) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
Duration duration = Duration(milliseconds: timeInMilliseconds);
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
if (duration.inHours > 0)
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
else
return "$twoDigitMinutes:$twoDigitSeconds";
}
String _printDate(Timestamp timestamp) {
var formattedAux = timestamp.toDate();
var day = formattedAux.day.toString().padLeft(2, '0');
var month = formattedAux.month.toString().padLeft(2, '0');
var year = formattedAux.year;
return '$year' + '-' + '$month' + '-' + '$day';
}
This screen is the one I am using background location and when I go back to MainApp, Streambuilder doesn't update and returns snapshot.data null:
import 'dart:async';
import 'package:carp_background_location/carp_background_location.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:provider/provider.dart';
import 'package:timer_builder/timer_builder.dart';
class LocationStreamWidget extends StatefulWidget {
#override
State<LocationStreamWidget> createState() => LocationStreamState();
}
enum LocationStatus { UNKNOWN, RUNNING, STOPPED }
class LocationStreamState extends State<LocationStreamWidget> {
final globalKey = GlobalKey<ScaffoldState>();
LocationManager locationManager = LocationManager.instance;
Stream<LocationDto> dtoStream;
StreamSubscription<LocationDto> dtoSubscription;
LocationStatus _status = LocationStatus.UNKNOWN;
// Definitions
List<LocationDto> _allPositions;
List<LocationDto> _positions = <LocationDto>[];
double totalDistance;
double intermediaryDist;
double distAux;
double distLatLong;
int totalTime;
int intermediaryTime;
int timeAux;
List<LocationDto> _allPositionsInter;
List<LocationDto> _positionsInter = <LocationDto>[];
final _firestore = FirebaseFirestore.instance;
#override
void initState() {
super.initState();
locationManager.interval = 1;
locationManager.distanceFilter = 0;
dtoStream = locationManager.dtoStream;
totalDistance = 0.0;
intermediaryDist = 0.0;
totalTime = 0;
intermediaryTime = 0;
_positions.clear();
}
void onData(LocationDto dto) {
setState(() {
if (_status == LocationStatus.UNKNOWN) {
_status = LocationStatus.RUNNING;
}
_positions.add(dto);
_allPositions = _positions;
print(_positions.length);
_positionsInter.add(dto);
_allPositionsInter = _positionsInter;
final firstTime = DateTime.fromMillisecondsSinceEpoch(
_allPositionsInter.first.time ~/ 1)
.toLocal();
final lastTime =
DateTime.fromMillisecondsSinceEpoch(_allPositionsInter.last.time ~/ 1)
.toLocal();
totalTime =
intermediaryTime + lastTime.difference(firstTime).inMilliseconds;
print(totalTime);
});
}
void start() async {
if (dtoSubscription != null) {
dtoSubscription.resume();
}
dtoSubscription = dtoStream.listen(onData);
await locationManager.start();
setState(() {
_status = LocationStatus.RUNNING;
});
}
void pause() async {
setState(() {
intermediaryDist = totalDistance;
intermediaryTime = totalTime;
_positionsInter.clear();
_allPositionsInter.clear();
_status = LocationStatus.STOPPED;
});
dtoSubscription.cancel();
await locationManager.stop();
}
String _printTime(int timeInMilliseconds) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
Duration duration = Duration(milliseconds: timeInMilliseconds);
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
if (duration.inHours > 0)
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
else
return "$twoDigitMinutes:$twoDigitSeconds";
}
#override
void dispose() {
if (dtoSubscription != null) {
dtoSubscription.cancel();
dtoSubscription = null;
_allPositions.clear();
_allPositionsInter.clear();
totalDistance = 0;
intermediaryDist = 0.0;
distAux = 0.0;
_status = LocationStatus.STOPPED;
totalTime = 0;
intermediaryTime = 0;
locationManager.start();
if (_positions != null) {
_positions.clear();
_positionsInter.clear();
_allPositionsInter.clear();
locationManager.start();
}
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return TimerBuilder.periodic(Duration(milliseconds: 500),
builder: (context) {
return Scaffold(
key: globalKey,
body: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Container(
child: FutureBuilder(
future: locationManager.checkIfPermissionGranted(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.data == false) {
return Text('No gps');
}
return Container(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'${totalTime != null ? _printTime(totalTime) : '0'}',
),
_getButtons(user),
],
),
),
);
}),
),
),
],
),
);
});
}
Widget _getButtons(UserClasse user) {
final _currentDate = _positions?.isNotEmpty ?? false
? (DateTime.fromMillisecondsSinceEpoch(_positions[0].time ~/ 1)
.toLocal())
: 0;
if (dtoSubscription == null)
return Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('Begin'),
color: Colors.blue,
onPressed: start),
),
],
),
);
else if (_status == LocationStatus.RUNNING)
return Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('Pause'),
color: Colors.blue,
onPressed: pause),
),
],
),
);
else if (_status == LocationStatus.STOPPED) {
return Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('Back'),
color: Colors.blue,
onPressed: start),
),
SizedBox(
width: 15,
),
Expanded(
child: RaisedButton(
child: Text('Stop'),
color: Colors.blue,
onPressed: () {},
onLongPress: () async {
_firestore
.collection('users')
.doc('user_id')
.collection('corridas')
.add({
'date': _currentDate,
'distance': 41.7,
'pace': 1244,
'average_speed': 12,
'runtime': 14134,
'max_speed': 41,
'max_altitude': 13,
'delta_altitude': 13,
'lat_first': -23.213,
'long_first': -23.213,
'polylines': 'feaf',
'markers': [1, 2, 3],
});
Navigator.of(context).pop();
},
),
),
],
),
);
} else
return Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Expanded(
child: RaisedButton(
child: Text('Start'),
color: Colors.blue,
onPressed: () {}),
),
],
),
);
}
}