Flutter - How to play a sequence of videos one after another? - flutter

How can I play a sequence of videos one after another with no breaks in between and no user action - just show videos one right after the other? (efficiently)
Here is what I tried:
VideoPlayer:
class MyVideoPlayer extends StatefulWidget {
MyVideoPlayer({
Key? key,
required this.onComplete,
}) : super(key: key);
final VoidCallback onComplete;
#override
MyVideoPlayerState createState() => MyVideoPlayerState();
}
class MyVideoPlayerState extends State<MyVideoPlayer> {
#override
void initState() {
super.initState();
controller.addListener(() {
if (!controller.value.isPlaying &&
controller.value.position.inSeconds >=
controller.value.duration.inSeconds) {
widget.onComplete();
}
});
}
#override
Widget build(BuildContext context) {
return VideoPlayer(
controller,
);
}
}
Main:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MyVideoPlayer(
onComplete: () {
_playVideo(asset);
},
);
}
}
_playVideo(String asset) {
controller =
VideoPlayerController.asset(asset);
controller.addListener(() {
setState(() {});
if (!controller.value.isPlaying &&
controller.value.position.inSeconds >=
controller.value.duration.inSeconds) {
}
});
controller.initialize().then((_) {
setState(() {});
controller.play();
if (mounted) setState(() {});
});
setState(() {
});
}
But onComplete gets accessed only after the first video and then no more. It should be called after each video is finished.
Any way of solving this will help a lot.

You can play multiple video by creating list of videos at initstate or in seperate datamodel and then changing the index of list to the controller.
this method also have some issue as mentioned in this question link
void playVideo() async {
if (DateTime.now().isAfter(videoData[index].start_from) &&
DateTime.now().isBefore(videoData[index].end_on)) {
bool played_count = await CountQuery().whenComplete(() {
});
file = File(videoData[index].file_link);
if (file.existsSync() && !played_count) {
isEnd = true;
controller = VideoPlayerController.file(file)
..initialize().then((value) {
setState(() {
controller.play();
});
controller.addListener(() {
// checking the duration and position every time
// Video Completed//
if (controller.value.duration == controller.value.position &&
isEnd) {
setState(() {
isEnd = false;
});
CreateLog("video");
controller.dispose();
PlayNext();
}
});
})
..setLooping(false);
} else {
PlayNext();
}
} else {
PlayNext();
}
}
void PlayNext() {
setState(() {
index++;
if (videoData.length > index) {
// this.asset = videoData[index].file_link;
this.file = File(videoData[index].file_link);
playVideo();
} else {
index = 0;
// this.asset = videoData[index].file_link;
this.file = File(videoData[index].file_link);
playVideo();
}
});
}

Related

flutter doesn't load more users don't know why

So I'm trying to load more users with pagination, it should only show 20 users and load new ones when the end of the screen is reached, but widget.loadMoreEndOfScroll gets returned null when it shouldn't, I don't know what else could be wrong, other times it worked a few times and than it stopped working and only showing 20 instead of loading new ones.
so this is the class what I use
class CAScaffold extends StatefulWidget {
const CAScaffold({
Key? key,
this.canLoadMoreEndOfScroll,
this.loadMoreEndOfScroll,
}) : super(key: key);
final bool? canLoadMoreEndOfScroll;
final Function? loadMoreEndOfScroll;
#override
State<CAScaffold> createState() => _CAScaffoldState();
}
this is the listener below
class _CAScaffoldState extends State<CAScaffold> {
ScrollController scrollUsersController = ScrollController();
final GlobalKey<ScaffoldState> _key = GlobalKey();
#override
void initState() {
scrollUsersController.addListener(() {
if(scrollUsersController.offset >= scrollUsersController.position.maxScrollExtent &&
!scrollUsersController.position.outOfRange &&
widget.canLoadMoreEndOfScroll != null && widget.canLoadMoreEndOfScroll!) {
print('endofscroll ${widget.loadMoreEndOfScroll}');
if(widget.loadMoreEndOfScroll != null) {
print('loadMoreEndOfScroll ${widget.loadMoreEndOfScroll}');
widget.loadMoreEndOfScroll!();
}
}
});
super.initState();
}
here it gets called and will call the getAllUsers function
#override
Widget build(BuildContext context) {
return CAScaffold(
canLoadMoreEndOfScroll: loadingUsers == false && allUsersLoaded == false,
loadMoreEndOfScroll: () {
getAllUsers();
newSearch = false;
},
pageTitle: 'Accounts',
child: Column(
children: <Widget>[
BlocConsumer<AccountScreenCubit, AccountScreenState>(
listener: (context, state) {
if(state is AccountScreenLoadingUsersState) {
loadingUsers = true;
}
if(state is AccountScreenUsersError) {
loadingUsers = false;
}
if(state is AccountScreenUsersLoaded) {
users.addAll(state.users);
print(state.users.length);
loadingUsers = false;
if(state.users.isEmpty) {
allUsersLoaded = true;
}
}
},
getAllUsers function
void getAllUsers() {
if(loadingUsers == false && allUsersLoaded == false) {
context.read<AccountScreenCubit>().getUsers(
searchTerm: searchInput,
orderFilter: dropDownValue,
newSearch: newSearch,
);
}
}
and than it gets send to the cubit
class AccountScreenCubit extends Cubit<AccountScreenState> {
AccountScreenCubit() : super(AccountScreenInitial());
QuerySnapshot? lastDocument;
Future<void> getUsers({String? searchTerm, bool newSearch = true, String? orderFilter}) async {
if(newSearch){
lastDocument = null;
}
try {
emit(AccountScreenLoadingUsersState());
await FirestoreUserHelper.getAllUsersOrderedByName(
20,
searchTerm: searchTerm,
orderFilter: orderFilter,
lastSnapshot: lastDocument,
).then((FirestoreReturn value) {
if(value.success) {
if(value.snapshot != null) {
lastDocument = value.snapshot;
}
emit(AccountScreenUsersLoaded(users: value.returnMain));
} else {
emit(AccountScreenUsersError(errorMsg: value.errorMessage));
}
});
} catch(e) {
emit(AccountScreenUsersError(errorMsg: 'Something went wrong: $e'));
}
}
}
if there is something I have missed let me know
You are not calling the getAllUsers function because you forgot the brackets, so add them.
loadMoreEndOfScroll: () {
getAllUsers();
newSearch = false;
},
You probably have to put CAScaffold inside BlocConsumer, otherwise parameter canLoadMoreEndOfScroll: loadingUsers == false && allUsersLoaded == false, is never updated.

How to update difference Time using Provider on flutter?

I have class:
class JWSModel extends ChangeNotifier {
String ketCounterShalat = "";
String diff = "";
void streamDifferentTime() {
DateTime now = DateTime.now();
if (now.isBefore(dhuhrTime) && now.isAfter(_dhuha)) {
ketCounterShalat = "Menuju dhuhur";
diff = now.difference(dhuhrTime).toString();
} else if (now.isBefore(asrTime) && now.isAfter(dhuhrTime)) {
ketCounterShalat = "Menuju ashar";
diff = now.difference(asrTime).toString();
} else if (now.isBefore(maghribTime) && now.isAfter(asrTime)) {
ketCounterShalat = "Menuju maghrib";
diff = now.difference(maghribTime).toString();
} else if (now.isBefore(ishaTime) && now.isAfter(maghribTime)) {
ketCounterShalat = "Menuju isya";
diff = now.difference(ishaTime).toString();
} else if (now.isBefore(sunriseTime) && now.isAfter(fajrTime)) {
diff = now.difference(sunriseTime).toString();
ketCounterShalat = "Menuju terbit";
} else if (now.isBefore(_dhuha) && now.isAfter(sunriseTime)) {
diff = now.difference(_dhuha).toString();
ketCounterShalat = "Menuju dhuha";
} else {
if (fajrTime.day == now.day) {
diff = now.difference(fajrTime).toString();
} else if (fajrTime.day != now.day) {
diff = now.difference(fajrTime.add(const Duration(days: 1))).toString();
}
ketCounterShalat = "Menuju subuh";
notifyListeners();
}
String get counterShalat => diff;
String get ketCounter => ketCounterShalat;
}
next, in the widget I created
class JWS extends StatefulWidget {
const JWS({Key? key}) : super(key: key);
#override
State<JWS> createState() => _JWSState();
}
class _JWSState extends State<JWS> {
JWSModel jws = JWSModel();
Future<void> getJWS() async {
jws.init();
}
#override
void initState() {
super.initState();
getJWS();
Timer.periodic(const Duration(seconds: 1), (timer) {
jws.streamDifferentTime();
});
}
#override
Widget build(BuildContext context) {
final state = context.watch<JWSModel>();
log(state.counterShalat);
Why not success? I want to update difference Time, I use a timer to update it. I don't want to use setState({}); because I think it will be hard to update every 1 second.
Update: if i log in class JWSModel success.
But, i call in class JWS nothing appears.
i want to get update different time but I don't want to use setState({});
or is there the right code for me to use.
class _JWSState extends State<JWS> {
#override
void initState() {
super.initState();
Timer.periodic(const Duration(seconds: 1), (timer) {
context.read<JWSModel>().streamDifferentTime();
});
}
#override
Widget build(BuildContext context) {
final state = context.watch<JWSModel>();
return Scaffold ( appBar: AppBar(),
body: Text(state.counterShalat,
style:TextStyle(color:Colors.blue)),
);
}
when any update from notifylistener, the value should be updated too.

Where to prevent re-animation of ListView.builder items when scrolling back to previously animated items?

I have a listview in which the text of items are animated when they first appear - and when they reappear after enough scrolling. When the list grows to certain size and the user scrolls back far enough items are animated again - presumably they've been removed from the widget tree and are now being re-inserted and thus get re-initiated etc. I want to prevent this from happening so that they only animate the first time they appear.
I think this means I need to have state stored somewhere per item that keeps track and tells the individual items whether they should animate on them being built or not. I am not sure where to put and how to connect that though, partly because it seems to overlap between presentation and business logic layers. I think perhaps it should be a variable in the list items contained in the list object that the listview builder is constructing from - or should it somehow be in the actual widgets in the listview?
class _StockListViewBuilderState extends State<StockListViewBuilder> with asc_alertBar {
final ScrollController _scrollController = ScrollController();
late double _scrollPosition;
late double _maxScrollExtent;
late bool isThisTheEnd = false;
_scrollListener() async {
setState(() {
_scrollPosition = _scrollController.position.pixels;
_maxScrollExtent = _scrollController.position.maxScrollExtent;
});
if (!isThisTheEnd && _scrollPosition / _maxScrollExtent > 0.90) {
isThisTheEnd = true;
if (widget.stockListicle.getIsRemoteEmpty()) {
alertBar('No more items available', /* null,*/ context: context);
} else {
await widget.stockListicle.fetch(numberToFetch: 5);
}
}
if (isThisTheEnd && _scrollPosition / _maxScrollExtent <= 0.90) {
isThisTheEnd = false;
}
}
#override
void initState() {
super.initState();
late String? userFullName = GetIt.I.get<Authenticate>().user?.fullName;
developer.log('Authenticated user $userFullName', name: '_StockListViewBuilderState');
developer.log("init ", name: "_StockListViewBuilderState ");
int listCount;
_scrollController.addListener(_scrollListener);
WidgetsBinding.instance.addPostFrameCallback((_) async {
//developer.log("stckLtcl init pf con ");
listCount = widget.stockListicle.items.length;
if (listCount < 10 && !widget.stockListicle.getIsRemoteEmpty()) {
try {
await widget.stockListicle.fetch(numberToFetch: 10);
} catch (e) {
super.setState(() {
//developer.log("Can't load stock:$e");
alertBar(
"Couldn't load from the internet.",
context: context,
backgroundColor: Colors.purple,
);
});
}
}
});
WidgetsBinding.instance.addPostFrameCallback((_) async {
final ConnectionNotifier connectionNotifier = context.read<ConnectionNotifier>();
if (connectionNotifier.isConnected() != true) {
await connectionNotifier.check();
if (connectionNotifier.isConnected() != true) {
alertBar("Please check the internet connection.", context: context);
}
}
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.vertical,
controller: _scrollController,
shrinkWrap: true,
key: widget.theKey,
itemCount: widget.stockListicle.items.length + 1,
itemBuilder: (context, index) {
if (index <= widget.stockListicle.items.length - 1) {
return InkWell(
onTap: (() => Navigator.pushNamed(
context,
'/stocks/stock',
arguments: ScreenArguments(widget.stockListicle.items[index] as Stock),
)),
child: StockListItem(
stock: widget.stockListicle.items[index] as Stock,
));
} else {
return LoadingItemNotifier(
isLoading: widget.stockListicle.getIsBusyLoading(),
);
}
},
);
}
}
//...
Currently StockListItem extends StatelessWidget and returns a 'ListTile' which as its title parameter has ...title: AnimatedText(textContent: stock.title),...
I was trying to keep track of first-time-animation inside AnimatedText widget until I realized from an OOP & Flutter perspective, it's probably wrong place...
class AnimatedText extends StatefulWidget {
final bool doShowMe;
final String textContent;
final Duration hideDuration;
final double durationFactor;
const AnimatedText({
Key? key,
this.doShowMe = true,
this.textContent = '',
this.hideDuration = const Duration(milliseconds: 500),
this.durationFactor = 1,
}) : super(key: key);
#override
State<AnimatedText> createState() => _AnimatedTextState();
}
class _AnimatedTextState extends State<AnimatedText> with SingleTickerProviderStateMixin {
late AnimationController _appearanceController;
late String displayText;
late String previousText;
late double durationFactor;
late Duration buildDuration = Duration(
milliseconds: (widget.textContent.length / 15 * widget.durationFactor * 1000).round());
#override
void initState() {
super.initState();
developer.log('init ${widget.textContent}', name: '_AnimatedTextState');
displayText = '';
previousText = widget.textContent;
_appearanceController = AnimationController(
vsync: this,
duration: buildDuration,
)..addListener(
() => updateText(),
);
if (widget.doShowMe) {
_doShowMe();
}
}
void updateText() {
String payload = widget.textContent;
int numCharsToShow = (_appearanceController.value * widget.textContent.length).ceil();
if (widget.doShowMe) {
// make it grow
displayText = payload.substring(0, numCharsToShow);
// developer.log('$numCharsToShow / ${widget.textContent.length} ${widget.textContent}');
} else {
// make it shrink
displayText = payload.substring(payload.length - numCharsToShow, payload.length);
}
}
#override
void didUpdateWidget(AnimatedText oldWidget) {
super.didUpdateWidget(oldWidget);
if ((widget.doShowMe != oldWidget.doShowMe) || (widget.textContent != oldWidget.textContent)) {
if (widget.doShowMe) {
_doShowMe();
} else {
_doHideMe();
}
}
if (widget.doShowMe && widget.textContent != previousText) {
previousText = widget.textContent;
developer.log('reset');
_appearanceController
..reset()
..forward();
}
}
#override
void dispose() {
_appearanceController.dispose();
displayText = '';
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _appearanceController,
builder: (context, child) {
return Text(displayText);
});
}
void _doShowMe() {
_appearanceController
..duration = buildDuration
..forward();
}
void _doHideMe() {
_appearanceController
..duration = widget.hideDuration
..reverse();
}
}

Pass Widgets in Connectivity check in flutter

I want to implement internet connectivity check into my app and I used official connectivity plugin and it is working great for displaying String Value but instead of showing string value in screen I want to display different widgets for connected and disconnected.
Here What I am Using
//
Widget result;
//
body: Container(
alignment: Alignment.center,
child: result != null ?
result : Text("unknown", style :
TextStyle(fontSize: 30,fontWeight: FontWeight.bold),
),
void checkStatus(){
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
if(
result == ConnectivityResult.mobile ||
result == ConnectivityResult.wifi){
Text("Connected", style:TextStyle(color:Colors.red));
} else {
Text("No InterNet", style:TextStyle(color:Colors.red));
}
});
}
#override
void initState() {
super.initState();
checkStatus();
}
And I am Getting 'unknown' value
try this
class Sample extends StatefulWidget {
#override
_SampleState createState() => _SampleState();
}
class _SampleState extends State<Sample> {
Widget result;
#override
void initState() {
super.initState();
checkStatus();
}
void checkStatus() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
result = Text("Connected to Mobile Network");
setState(() {});
} else if (connectivityResult == ConnectivityResult.wifi) {
result = Text("Connected to WiFi");
print("Connected to WiFi");
setState(() {});
} else {
result = Text("Unable to connect. Please Check Internet Connection");
setState(() {});
print("Unable to connect. Please Check Internet Connection");
}
}
#override
Widget build(BuildContext context) {
return Center(child: result);
}
}
Try this:
Use this package for checking Internet:
data_connection_checker:
And, Inside your stateful class create stream listener i.e and a boolean value.
StreamSubscription<DataConnectionStatus> listener; bool isConnected = true;
and Inside initState:
#override
void initState() {
super.initState();
listener = DataConnectionChecker().onStatusChange.listen((status) {
switch (status) {
case DataConnectionStatus.connected:
print('Data connection is available. $status');
setState(() {
isConnected = true;
});
break;
case DataConnectionStatus.disconnected:
print('You are disconnected from the internet. $status');
setState(() {
isConnected = false;
});
break;
}
});
}
Done, This will keep listening to changes in your internet status, Thus you can prompt user as you like. Cheers, Feel free to ask if confusion and if it helps upvote :D

inheritFromWidgetOfExactType(InheritedProvider<ConnectivityStatus>) or inheritFromElement() was called before

In this simple class i want to make base state class to manage some actions such as accessing to internet connection:
abstract class BaseState<T extends StatefulWidget> extends State {
bool isOnline;
ConnectivityStatus _connectivityStatus;
#override
void initState() {
super.initState();
_connectivityStatus = Provider.of<ConnectivityStatus>(context);
isOnline = _connectivityStatus == ConnectivityStatus.Connected;
if (!isOnline) {
showSimpleNotification(Text("disconnected"), background: Colors.green);
} else {
showSimpleNotification(Text("connected"), background: Colors.red);
}
}
}
when i try to use this class like with:
class _FragmentLoginState extends BaseState<FragmentLogin> with SingleTickerProviderStateMixin {
PageController _pageController;
Color left = Colors.black;
Color right = Colors.white;
#override
void initState() {
super.initState();
_pageController = PageController(initialPage: 1);
}
#override
Widget build(BuildContext context) {
}
}
the problem is you don't have a valid Context yet.
you can try theses two solution
defer using context:
#override
void initState() {
super.initState();
_initConnectivity();
}
Future _initConnectivity() async {
await Future.delayad(Duration.zero);
_connectivityStatus = Provider.of<ConnectivityStatus>(context);
isOnline = _connectivityStatus == ConnectivityStatus.Connected;
if (!isOnline) {
showSimpleNotification(Text("disconnected"), background: Colors.green);
} else {
showSimpleNotification(Text("connected"), background: Colors.red);
}
}
move your logic in the build function ( in BaseState )
_initConnectivity() {
_connectivityStatus = Provider.of<ConnectivityStatus>(context);
isOnline = _connectivityStatus == ConnectivityStatus.Connected;
if (!isOnline) {
showSimpleNotification(Text("disconnected"), background: Colors.green);
} else {
showSimpleNotification(Text("connected"), background: Colors.red);
}
}
#override
Widget build(BuildContext context) {
_initConnectivity();
}
in the second method you can also create a flag like isFirstBuild to make sure it runs only one time.
also these may help :
initialize data once in initState and call the setState when data is ready causes exception
Flutter get context in initState method
Use a frame callback which delays execution until the next frame (i.e. after initState has complete)
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
_connectivityStatus = Provider.of<ConnectivityStatus>(context);
isOnline = _connectivityStatus == ConnectivityStatus.Connected;
if (!isOnline) {
showSimpleNotification(Text("disconnected"), background: Colors.green);
} else {
showSimpleNotification(Text("connected"), background: Colors.red);
}
});
}