How to update difference Time using Provider on flutter? - 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.

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.

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();
}
}

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

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();
}
});
}

How to access Riverpod StateNotifier state outside build without ConsumerWidget or HookWidget?

I have this class:
class BeatCounter extends StateNotifier<int> {
BeatCounter() : super(8);
int get counter => state;
void increment() {
if (state < 16) {
state++;
print('State $state');
} else
return;
}
void decrement() {
if (state > 1) {
state--;
} else
return;
}
}
final beatCounterProvider = StateNotifierProvider((ref) => BeatCounter());
And want to access the state inside a class that extends a StatefullWidget that I don't want (/know how to) change. So I can't use 'with' to extend 'ConsumerWidget' or 'HookWidget'.
How do I get the state in this class?
class ChordsTrack extends BaseWidget {
ChordsTrack({Key key, #required this.sample}) : super(key: key);
final SOUND_SAMPLE sample;
#override
_ChordsTrackState createState() => _ChordsTrackState();
}
class _ChordsTrackState extends BaseState<ChordsTrack> {
MultitrackChordBassBoxCreator multitrackBox =
MultitrackChordBassBoxCreator();
List<bool> _data = List.generate(***BeatCounter().state***, (i) => false);
#override
void on<Signal>(Signal signal) {
setState(() => _data = AudioEngine.trackdata[widget.sample]);
}
...
}
Newbie question, I know, but would really appreciate some help.
I solved it this way.
class BeatCounter extends StateNotifier<int> {
BeatCounter() : super(8);
static int counter = 8; //*
void increment() {
if (state < 16) {
state++;
counter++; //*
} else
return;
}
void decrement() {
if (state > 1) {
state--;
counter--;//*
} else
return;
}
}
Is there a better option?
Just use context.read(beatCounterProvider) in your initState.
class _ChordsTrackState extends BaseState<ChordsTrack> {
MultitrackChordBassBoxCreator multitrackBox =
MultitrackChordBassBoxCreator();
List<bool> _data;
#override
void initState(){
super.initState();
_data = List.generate(context.read(beatCounterProvider).state, (i) => false);
}
#override
void on<Signal>(Signal signal) {
setState(() => _data = AudioEngine.trackdata[widget.sample]);
}
...
}

Flutter Riverpod StateNotifierProvider: only watch changes for part of a model

I have a model like this:
class TimerModel {
const TimerModel(this.timeLeft, this.buttonState);
final String timeLeft;
final ButtonState buttonState;
}
enum ButtonState {
initial,
started,
paused,
finished,
}
And here is the StateNotifierProvider:
class TimerNotifier extends StateNotifier<TimerModel> {
TimerNotifier() : super(_initialState);
static const int _initialDuration = 10;
static final _initialState = TimerModel(
_durationString(_initialDuration),
ButtonState.initial,
);
final Ticker _ticker = Ticker();
StreamSubscription<int> _tickerSubscription;
void start() {
if (state.buttonState == ButtonState.paused) {
_tickerSubscription?.resume();
state = TimerModel(state.timeLeft, ButtonState.started);
} else {
_tickerSubscription?.cancel();
_tickerSubscription =
_ticker.tick(ticks: _initialDuration).listen((duration) {
state = TimerModel(_durationString(duration), ButtonState.started);
});
_tickerSubscription.onDone(() {
state = TimerModel(state.timeLeft, ButtonState.finished);
});
state =
TimerModel(_durationString(_initialDuration), ButtonState.started);
}
}
static String _durationString(int duration) {
final String minutesStr =
((duration / 60) % 60).floor().toString().padLeft(2, '0');
final String secondsStr =
(duration % 60).floor().toString().padLeft(2, '0');
return '$minutesStr:$secondsStr';
}
void pause() {
_tickerSubscription?.pause();
state = TimerModel(state.timeLeft, ButtonState.paused);
}
void reset() {
_tickerSubscription?.cancel();
state = _initialState;
}
#override
void dispose() {
_tickerSubscription?.cancel();
super.dispose();
}
}
class Ticker {
Stream<int> tick({int ticks}) {
return Stream.periodic(Duration(seconds: 1), (x) => ticks - x - 1)
.take(ticks);
}
}
I can listen for all changes in state like this:
final timerProvider = StateNotifierProvider<TimerNotifier>((ref) => TimerNotifier());
However I want to make another provider that only listens for changes in the ButtonState. This doesn't work:
final buttonProvider = StateProvider<ButtonState>((ref) {
return ref.watch(timerProvider.state).buttonState;
});
because it still returns all the state changes.
This also doesn't work:
final buttonProvider = StateProvider<ButtonState>((ref) {
return ref.watch(timerProvider.state.buttonState);
});
Because the state object doesn't have a buttonState property.
How do I only watch buttonState changes?
Using watch gives a new state whenever the watched state changes. So can solve the problem in two parts like so:
final _buttonState = Provider<ButtonState>((ref) {
return ref.watch(timerProvider.state).buttonState;
});
Using this provider will cause a rebuild every time the timerProvider.state changes. However, the trick is to do the following:
final buttonProvider = Provider<ButtonState>((ref) {
return ref.watch(_buttonState);
});
Since _buttonState will be the same for most of the timerProvider.state changes, watching _buttonState will only cause rebuilds when _buttonState actually changes.
Thanks to this post for showing the answer. That post also indicates that there will be a simplified syntax soon:
final buttonState = ref.watch(timerProvider.state.select((state) => state.buttonState));