Call a void function from another file - flutter

I'm creating an app that has a start button (class StartButton) and i want that button calls a method from another class (class NewRide), but how can i call a void function from another file?
class StartButton extends StatefulWidget{
#override
_StartButtonState createState() => _StartButtonState();
}
class _StartButtonState extends State<StartButton> {
String _driverState = 'offline';
#override
Widget build(context){
return FlatButton(
child: Text('Start'),
onPressed: (){
setState(() {
if (_driverState == 'offline') {
_driverState = 'online';
} else {
_driverState = 'offline';
}
});
},
);
}
}
This is New Ride class, it has the void function i want to call when i press start button.
class NewRide extends StatefulWidget {
#override
_NewRideState createState() => _NewRideState();
}
class _NewRideState extends State<NewRide> {
int _counter = 60;
Timer _timer;
#override
Widget build(BuildContext context) {
void startTimer() {
_counter = 60;
if (_timer != null) {
_timer.cancel();
}
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
if (_counter > 0) {
_counter--;
} else {
_timer.cancel();
}
});
});
}
_showNewRideAlert() {
if (_counter == 0) {
return Text('Counter == 0');
} else {
return Text('Counter != 0');
}
}
return _showNewRideAlert();
}
}

use GlobalKey and make startTimer public and change NewRideState to _ NewRideState
class StartButton extends StatefulWidget {
#override
_StartButtonState createState() => _StartButtonState();
}
class _StartButtonState extends State<StartButton> {
String _driverState = 'offline';
GlobalKey<NewRideState> newRideKey = GlobalKey<NewRideState>();
#override
Widget build(context) {
return Column(
children: [
NewRide(key: newRideKey),
FlatButton(
child: Text('Start'),
onPressed: () {
setState(() {
if (_driverState == 'offline') {
_driverState = 'online';
} else {
_driverState = 'offline';
}
});
},
),
],
);
}
}
class NewRide extends StatefulWidget {
const NewRide({Key key}) : super(key: key);
#override
NewRideState createState() => NewRideState();
}
class NewRideState extends State<NewRide> {
int _counter = 60;
Timer _timer;
#override
Widget build(BuildContext context) {
_showNewRideAlert() {
if (_counter == 0) {
return Text('Counter == 0');
} else {
return Text('Counter != 0');
}
}
return _showNewRideAlert();
}
void startTimer() {
_counter = 60;
if (_timer != null) {
_timer.cancel();
}
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
if (_counter > 0) {
_counter--;
} else {
_timer.cancel();
}
});
});
}
}

First import the other file/class into the main class (I'm not sure what your file is called).
import '.../newRide.dart' as newRide;
Then call the function like this:
newRide._showNewRideAlert();
It might not work due to how the class/state is setup though (the widget won't be built). So you may have to move the code into a place that is callable, such as the top of the class or implement the function into the page where it is used.

Related

building a countdown clock inside a provider package change notifier

I have built a working countdown clock that displays the time remaining in the format, hours:minutes:senconds. inside a stateful widget. that uses a fullscreen inkwell to start and stop it.
What I want to do is transfer this code to a change notifier. (I already a change notifier setup ready to go called CountdownTimers) so i can see this countdown running from multiple pages on my app.
Here is the code for the working countdown clock in the stateful widget:
class ChessClock2 extends StatefulWidget {
const ChessClock2({Key? key}) : super(key: key);
#override
State<ChessClock2> createState() => _ChessClock2State();
}
class _ChessClock2State extends State<ChessClock2> {
static const countdownDuration = Duration(hours: 5, minutes: 10, seconds: 10);
Duration duration = Duration();
Timer? timer;
bool beenPressed = false;
#override
void initState() {
super.initState();
Reset();
}
void Reset() {
setState(() => duration = countdownDuration);
}
void AddTime() {
final minusSeconds = -1;
setState(() {
final seconds = duration.inSeconds + minusSeconds;
if (seconds < 0) {
timer?.cancel();
} else {
duration = Duration(seconds: seconds);
}
});
}
void StartTimer() {
timer = Timer.periodic(
Duration(seconds: 1),
(_) => AddTime(),
);
}
void StopTimer() {
setState(() => timer?.cancel());
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: InkWell(
onTap: () {
setState(() {
beenPressed = !beenPressed;
});
beenPressed ? StartTimer() : StopTimer();
},
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: beenPressed ? kOrange : kBlueGrey900,
borderRadius: BorderRadius.circular(30),
),
child: Center(
child: TimeDisplay(),
),
),
),
),
);
}
Widget TimeDisplay() {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = twoDigits(
duration.inHours.remainder(60),
);
final minutes = twoDigits(
duration.inMinutes.remainder(60),
);
final seconds = twoDigits(
duration.inSeconds.remainder(60),
);
return Text(
'$hours:$minutes:$seconds',
style: TextStyle(fontSize: 50),
);
}
}
When I transfer the code over, I'm running into trouble because I can't use setState in the change notifier and I'm unsure how to translate the code to get it to work.
so far, by moving the individual variables over as well as the widget TimDisplay, I'm able to get the timer to display correctly from the change notifier but am not sure how to get it to work from the change notifier.
here is where I am now:
type hereclass ChessClock3 extends StatefulWidget {
const ChessClock3({Key? key}) : super(key: key);
#override
State<ChessClock3> createState() => _ChessClock3State();
}
class _ChessClock3State extends State<ChessClock3> {
#override
void initState() {
super.initState();
Reset();
}
void Reset() {
setState(() => duration = countdownDuration);
}
void AddTime() {
final minusSeconds = -1;
setState(() {
final seconds = duration.inSeconds + minusSeconds;
if (seconds < 0) {
timer?.cancel();
} else {
duration = Duration(seconds: seconds);
}
});
}
void StartTimer() {
timer = Timer.periodic(
Duration(seconds: 1),
(_) => AddTime(),
);
}
void StopTimer() {
setState(() => timer?.cancel());
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: InkWell(
onTap: () {
setState(() {
context.read<CountdownTimers>().BeenPressed();
});
context.read<CountdownTimers>().beenPressed
? StartTimer()
: StopTimer();
},
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(
color: context.watch<CountdownTimers>().beenPressed
? kKillTeamOrange
: kBlueGrey900,
borderRadius: BorderRadius.circular(30),
),
child: Center(
child: context.read<CountdownTimers>().TimeDisplay(),
),
),
),
),
);
}
}
class CountdownTimers with ChangeNotifier {
Duration _countdownDuration = Duration(hours: 5, minutes: 10, seconds: 10);
Duration _duration = Duration();
Timer? timer;
bool _beenPressed = false;
Duration get countdownDuration => _countdownDuration;
Duration get duration => _duration;
bool get beenPressed => _beenPressed;
void BeenPressed() {
_beenPressed = !_beenPressed;
}
Widget TimeDisplay() {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = twoDigits(
_duration.inHours.remainder(60),
);
final minutes = twoDigits(
_duration.inMinutes.remainder(60),
);
final seconds = twoDigits(
_duration.inSeconds.remainder(60),
);
return Text(
'$hours:$minutes:$seconds',
style: TextStyle(fontSize: 50),
);
}
}
If anyone can show me how to translate the code over It would be very much appreciated.
thanks so much!
I would use Flutter Riverpod instead like the code below. In Riverpod it's not recommended to use Change Notifier. Even in complexe application. To change the state of Change Notifier you must call notifyListeners.
import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final countDownControllerProvider = StateNotifierProvider.family
.autoDispose<CountdownController, Duration, Duration>((ref, initialDuration) {
return CountdownController(initialDuration);
});
class CountdownController extends StateNotifier<Duration> {
Timer? timer;
final Duration initialDuration;
CountdownController(this.initialDuration) : super(initialDuration) {
startTimer();
}
void startTimer() {
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (state == Duration.zero) {
timer.cancel();
} else {
if (mounted) {
state = state - const Duration(seconds: 1);
} else {
timer.cancel();
}
}
});
}
void stopTimer() {
timer?.cancel();
}
void resetTimer({required Duration initialDuration}) {
stopTimer();
state = initialDuration;
startTimer();
}
void addTime({required Duration duration}) {
state = state + duration;
}
void subtractTime({required Duration duration}) {
state = state - duration;
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
}
Then in the widget
Consumer(builder: (context, ref, _) {
return Text(
ref
.watch(countDownControllerProvider(
const Duration(
days: 25,
hours: 15,
minutes: 36,
seconds: 45)))
.formatDuration(),
style: context.theme.textTheme.bodyText1!
.copyWith(color: Colors.white),
);
})
And finally, don't hesitate to put your conversion logic Duration to String, into a extension
extension DurationExtensions on Duration {
String formatDuration() {
int seconds = inSeconds;
final days = seconds~/Duration.secondsPerDay;
seconds -= days*Duration.secondsPerDay;
final hours = seconds~/Duration.secondsPerHour;
seconds -= hours*Duration.secondsPerHour;
final minutes = seconds~/Duration.secondsPerMinute;
seconds -= minutes*Duration.secondsPerMinute;
final List<String> tokens = [];
if (days != 0) {
tokens.add('${days}d');
}
if (tokens.isNotEmpty || hours != 0){
tokens.add('${hours}h');
}
if (tokens.isNotEmpty || minutes != 0) {
tokens.add('${minutes}min');
}
if(tokens.isNotEmpty || seconds != 0) {
tokens.add('${seconds}s');
}
return tokens.join('');
}
}
The trick you are looking for is the function notifyListeners() of a ChangeNotifier. If you used context.watch() inside your widget to watch the ChangeNotifier, you will be updated and the widget is rebuild if necessary.
A short example might look like this
ConsumingWidget.dart
#override
Widget build(BuildContext context) {
var timeProvider = context.watch<TimeProvider>();
int counter = timeProvider.counter;
return Text("${counter} seconds have passed");
}
In this case, you would need to provide an instance of TimeProvider above your main widget in the widget tree via ChangeNotifierProvider.
You can then let the widget rebuild (if .counter changed) via notifyListeners()
ParentWidget.dart
#override
Widget build(BuildContext context) {
ChangeNotifierProvider(
create: (_) => TimeProvider(),
child: ConsumingWidget()
);
}
Provider.dart
class TimeProvider extends ChangeNotifier {
Timer? timer;
final Duration initialDuration;
int counter = 0;
CountdownController(this.initialDuration) : super(initialDuration) {
startTimer();
}
void startTimer() {
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
counter += 1; // Change some data based on the timer. Just an example here
notifyListeners(); // Then update listeners!
});
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
This is the built-in solution of the Provider package. Starting from this basic pattern, you can then look into more sophisticated state management solutions like Riverpod, Bloc etc.

Flutter Bloc re-emitting previous state automatically

I wanted to make a timer based on the Internet cubit. when Internet Disconnected State is emitted timer should be reset. But after reset(when Internet Disconnect state emitted) it is emitting increment timer state again.
Home Cubit (timer cubit)
class HomeCubit extends Cubit<HomeState> {
HomeCubit() : super(HomeState.initial());
void increment() {
Timer.periodic(const Duration(seconds: 1), (timer) {
emit(state.copyWith(counter: state.counter + 1));
});
}
void reset() {
emit(
state.copyWith(counter: 0),
);
}
}
Home State (timer state)
class HomeState extends Equatable {
final int counter;
const HomeState({
required this.counter,
});
factory HomeState.initial() {
return const HomeState(counter: 0);
}
HomeState copyWith({
int? counter,
}) {
return HomeState(
counter: counter ?? this.counter,
);
}
#override
List<Object?> get props => [counter];
}
Internet Cubit
class InternetCubit extends Cubit<InternetState> {
final Connectivity connectivity;
late StreamSubscription internetStreamSubscription;
InternetCubit({required this.connectivity}) : super(InternetInitial()) {
monitorInternet();
}
void monitorInternet() {
internetStreamSubscription = connectivity.onConnectivityChanged.listen((connectivityResult) {
if (connectivityResult == ConnectivityResult.wifi) {
emit(const InternetConnected(connectionType: ConnectionType.wifi));
} else if (connectivityResult == ConnectivityResult.mobile) {
emit(const InternetConnected(connectionType: ConnectionType.mobileData));
} else if (connectivityResult == ConnectivityResult.none) {
emit(InternetDisconnected());
}
});
}
#override
Future<void> close() {
internetStreamSubscription.cancel();
return super.close();
}
}
Internet State
enum ConnectionType { wifi, mobileData }
abstract class InternetState extends Equatable {
const InternetState();
#override
List<Object> get props => [];
}
class InternetInitial extends InternetState {}
class InternetConnected extends InternetState {
final ConnectionType connectionType;
const InternetConnected({required this.connectionType});
#override
List<Object> get props => [connectionType];
}
class InternetDisconnected extends InternetState {}
Homepage view
class HomePage extends StatelessWidget {
const HomePage({super.key});
#override
Widget build(BuildContext context) {
return BlocListener<InternetCubit, InternetState>(
listener: (context, state) {
if (state is InternetConnected) {
context.read<HomeCubit>().increment();
} else if (state is InternetDisconnected) {
context.read<HomeCubit>().reset();
}
},
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('You have been connected to the Internet for the past'),
Text(context.watch<HomeCubit>().state.counter.toString()),
],
),
),
),
);
}
}
I tried Cubit stream subscribe too. but why it is re-emitting old state again? why timer not staying in 0 or reset state?
If I understand you correctly you want the timer to stay 0 when you call reset() in HomeCubit().
Try this:
class HomeCubit extends Cubit<HomeState> {
HomeCubit() : super(HomeState.initial());
Timer? timer;
void increment() {
timer ??= Timer.periodic(const Duration(seconds: 1), (timer) {
emit(state.copyWith(counter: state.counter + 1));
});
}
void reset() {
timer?.cancel();
timer = null;
emit(
state.copyWith(counter: 0),
);
}
}

Save Page State in Navigation

Trying to Navigate back and forth between SignIn and ForgotPassword Page using Navigation.push().
But the Forgot Password Page Keep Losing its state (the countdown timer here).
Home Page for App. Home
When I enter email it shows a message and a counter (goes from 30 to 0). Page State
I go back to SignIn page and come back the timer and message are gone. State Gone
How to save a state for Navigation.push() page?
Forgot.dart
class ForgotPassword extends StatefulWidget {
const ForgotPassword({Key? key}) : super(key: key);
#override
_ForgotPasswordState createState() => _ForgotPasswordState();
}
class _ForgotPasswordState extends State<ForgotPassword> {
TextEditingController emailC = TextEditingController();
final formkey = GlobalKey<FormState>();
AuthMethods authMethods = AuthMethods();
bool sent = false;
Timer? _timer;
int _start = 0;
void startTimer() async {
if (formkey.currentState!.validate() && _start == 0) {
_start = 30;
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) {
if (_start == 0) {
setState(() {
timer.cancel();
});
} else {
setState(() {
_start--;
});
}
},
);
await authMethods.resetPassword(emailC.text);
}
}
#override
void dispose() {
_timer!.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(... rest of code
body: PageView(
controller: _pageController,
physics: NeverScrollableScrollPhysics(),
children: [
FirstScreen(),
SecondScreen(),
],
),
replace Navigator.push()/pop() with _pageController.goToPage(0 or 1)

how to delay data view 3 second in flutter

this my code
Container(
child: Column(
children: jobProvider.jobs
.map(
(job) => JobTile(job),
)
.toList(),
),
)
how to load this data delay for 3 seconds ? here I display the listview data, before displaying it I want to display, the
return Container(
child: ProfileShimmer(),
);
Should be as easy as this:
import 'package:flutter/material.dart';
class PageWithDelayedView extends StatefulWidget {
const PageWithDelayedView({Key? key}) : super(key: key);
#override
_PageWithDelayedViewState createState() => _PageWithDelayedViewState();
}
class _PageWithDelayedViewState extends State<PageWithDelayedView> {
bool _initialized = false;
#override
void initState() {
super.initState();
// Schedule function call after the widget is ready to display
WidgetsBinding.instance?.addPostFrameCallback((_) {
_initialize();
});
}
void _initialize() {
Future<void>.delayed(const Duration(seconds: 3), () {
if (mounted) { // Check that the widget is still mounted
setState(() {
_initialized = true;
});
}
});
}
#override
Widget build(BuildContext context) {
if (!_initialized) {
return Text('Hold on a bit');
}
return Text('Yay, I\'m ready!');
}
}

I need to know if a key was down (pressed) while the user clicked on a button

In a Flutter Desktop app, I want to know if, when a user clicks on a button with the mouse, they were also holding down a key (like Shift, Control, Alt etc).
How can this be done?
EDIT
My initial question wasn't clear enough.
I have a dynamic list of checkboxes and I want to use SHIFT+click to select everything between the last selected one and the one that was selected with SHIFT down.
I have looked at FocusNode but that seems to only work for 1 element.
This can be done with a FocusNode.
You'll need a stateful widget where you can use initialize the node. You need to attach the node and define the callback that is called on keyboard presses. Then you can request focus from the node with requestFocus so that the node receives the keyboard events.
You'll also need to call _nodeAttachment.reparent(); in your build method. You should also dispose the node in dispose.
The example below prints true or false for whether the shift key is pressed when the button is pressed. This can be easily expanded to other keys like control and alt with the isControlPressed and isAltPressed properties.
Full example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late final FocusNode focus;
late final FocusAttachment _nodeAttachment;
bool isShiftPressed = false;
#override
void initState() {
super.initState();
focus = FocusNode(debugLabel: 'Button');
_nodeAttachment = focus.attach(context, onKey: (node, event) {
isShiftPressed = event.isShiftPressed;
});
focus.requestFocus();
}
#override
void dispose() {
focus.dispose();
super.dispose();
}
Widget build(BuildContext context) {
_nodeAttachment.reparent();
return TextButton(
onPressed: () {
print(isShiftPressed);
},
child: Text('Test'),
);
}
}
You can still use this solution for your more specific problem. Wrap the above example around your list of checkboxes. You can do a bit of simple logic to get your intended behavior. If what I have here is not exact, you should be able to easily modify it to your needs. This proves that you can use this method for your need, however, even if some details in the logic are not exact:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late final FocusNode focus;
late final FocusAttachment _nodeAttachment;
bool isShiftPressed = false;
List<bool> checkboxStates = List.filled(5, false);
int lastClicked = -1;
#override
void initState() {
super.initState();
focus = FocusNode(debugLabel: 'Button');
_nodeAttachment = focus.attach(context, onKey: (node, event) {
isShiftPressed = event.isShiftPressed;
});
focus.requestFocus();
}
#override
void dispose() {
focus.dispose();
super.dispose();
}
Widget build(BuildContext context) {
_nodeAttachment.reparent();
return Column(
children: List.generate(checkboxStates.length, (index) => Checkbox(
value: checkboxStates[index],
onChanged: (val) {
if(val == null) {
return;
}
setState(() {
if(isShiftPressed && val) {
if(lastClicked >= 0) {
bool loopForward = lastClicked < index;
if(loopForward) {
for(int x = lastClicked; x < index; x++) {
checkboxStates[x] = true;
}
}
else {
for(int x = lastClicked; x > index; x--) {
checkboxStates[x] = true;
}
}
}
}
checkboxStates[index] = val;
});
if(val) {
lastClicked = index;
}
else {
lastClicked = -1;
}
print('Checkbox $index: $isShiftPressed');
}
)),
);
}
}