I am making a login app where by i have created an OTP validation page. In this page i want to make a resend option which is clickable only after 30 seconds of page loading and once clicked becomes unclickable for ever.
I am new to flutter so I am sorry if this seems trivial.
You can follow this code.
class TestButton extends StatefulWidget {
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> {
bool firstStateEnabled = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
Timer(Duration(milliseconds: 30000), () {
setState(() {
firstStateEnabled = true;
});
});
return Scaffold(
body: Container(
child: firstStateEnabled
? Center(
child: Container(
width: 200,
height: 55,
child: RaisedButton(
onPressed: () {},
child: Text("Resend OTP"),
),
),
)
: Center(child: Container()),
),
);
}
}
Or if you need only one time the button than you can follow below codes.
Install timer_count_down.
Then, below code.
class TestButton extends StatefulWidget {
#override
_TestButtonState createState() => _TestButtonState();
}
class _TestButtonState extends State<TestButton> {
bool firstStateEnabled = false;
final CountdownController controller = CountdownController();
final int seconds = 30;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
child: firstStateEnabled
? Center(
child: Container(
width: (200),
height: 55,
child: RaisedButton(
onPressed: () {
setState(() {
firstStateEnabled = false;
});
},
child: Text("Resend OTP"),
),
),
)
: Center(child: Container()),
),
Countdown(
controller: controller,
seconds: seconds,
build: (context, double time) {
return Container();
},
interval: Duration(milliseconds: 100),
onFinished: () {
setState(() {
firstStateEnabled = true;
;
});
},
)
],
),
);
}
}
Related
If nothing is done on the screen, I want to print something on the screen some time after the last action. How can I do that? How can I check the screen click status?
You can wrap Scaffold with GestureDetector and use onPanDown to capture the screen event, onTap doesn't win on hit test if there are inner clickable buttons. Also use behavior: HitTestBehavior.translucent,
Another notable thing is here, it is needed to be check on every second, because the checkup unit is on second. You can create a wrapper widget from it.
class ScreenT extends StatefulWidget {
const ScreenT({Key? key}) : super(key: key);
#override
State<ScreenT> createState() => _ScreenTState();
}
class _ScreenTState extends State<ScreenT> {
#override
void dispose() {
timer?.cancel();
super.dispose();
}
Timer? timer;
int maxDelaySec = 10;
int idleScreenCounter = 0;
#override
void initState() {
super.initState();
initTimer();
}
initTimer() {
timer = Timer.periodic(Duration(seconds: 1), (timer) {
idleScreenCounter++;
setState(() {}); //
});
}
onScreenTap() {
print("tapped on Screen");
idleScreenCounter = 0;
setState(() {});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onPanDown: (_) => onScreenTap(),
child: Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => SizedBox(
width: constraints.maxWidth,
height: constraints.maxHeight,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (maxDelaySec - idleScreenCounter > 0)
SizedBox(
height: 200,
child: Text(
" Tap the screen within ${maxDelaySec - idleScreenCounter}"),
),
if (maxDelaySec - idleScreenCounter < 0)
Container(
height: 100,
width: 100,
color: Colors.cyanAccent,
child: Text("Tap on screen"),
),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
print("An action");
},
child: Text("A Button"),
),
ElevatedButton(
onPressed: () {
print("act");
},
child: Text("Elev"),
)
],
),
),
),
),
),
);
}
}
A naive approach could involve a Timer with dart:async.
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: _SomeWidget(),
);
}
}
class _SomeWidget extends StatefulWidget {
const _SomeWidget();
#override
State<_SomeWidget> createState() => _SomeWidgetState();
}
class _SomeWidgetState extends State<_SomeWidget> {
late Timer _timer;
#override
void initState() {
super.initState();
// It's up to you if you want the timer to start immediately with some effects or not.
_timer = Timer(const Duration(seconds: 1), () {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
// i.e. from the first interaction and so on
_timer.cancel();
_timer = Timer(const Duration(seconds: 1), () {
if (mounted) {
// !
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Some message')),
);
}
});
},
child: const Center(child: Text('My screen contents')),
),
);
}
}
The mounted check is very important, as Timer introduces an async gap, which may be dangerous when using context.
You can add a Gesture detector at the top level and start a timer on tap and on completion you can fire an event like the following
GestureDetector(
onTap: (){
startTimer();
}
child: Column(
children:[
//all other widgets
]
)
),
Then to define the timer
late Timer _timer;
void startTimer()
{
if(_timer != null && _timer.isActive) _timer.cancel();
_timer = Timer(
const Duration(seconds: 30),
() {
print("inactive for 30 seconds");
},
);
}
here in this case each time the user taps on the screen the timer is restarted and on 30th second the print is fired.
I'm currently create chat in flutter and get the last messages , I want to handle when scrolling to top to load more messages how can I create that ?
If you want to implement swipe to refresh kind of behaviour, you can use RefreshIndicator. See the example and usage in this YouTube video.
All you have to do is wrap your scrollable widget (it can be ListView or SingleChildScrollView) in a RefreshIndicator and provide onRefresh method:
class PullToRefresh extends StatelessWidget {
const PullToRefresh({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: _refreshData,
child: ListView.builder( // or SingleChildScrollView
...
),
);
}
Future<void> _refreshData() async {
// load more items
}
}
ListView reverse: true displays the List from the bottom to the top.
and this is how to implement pagination
class HomeState extends State<Home> {
ScrollController? controller;
final _all = <WordPair>[];
final _saved = Set<WordPair>();
final _biggerFont = const TextStyle(fontSize: 18.0);
GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
bool isLoading = false;
#override
void initState() {
super.initState();
_all.addAll(generateWordPairs().take(20));
controller = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
super.dispose();
controller?.dispose();
}
void _scrollListener() {
if (controller!.position.pixels == controller!.position.maxScrollExtent) {
startLoader();
}
}
void startLoader() {
setState(() {
isLoading = !isLoading;
fetchData();
});
}
fetchData() async {
var _duration = const Duration(seconds: 2);
return Timer(_duration, onResponse);
}
void onResponse() {
setState(() {
isLoading = !isLoading;
_all.addAll(generateWordPairs().take(20));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: const Text(
"List load more example",
style: TextStyle(color: Colors.white),
),
),
body: Stack(
children: <Widget>[
_buildSuggestions(),
_loader(),
],
),
);
}
Widget _buildRow(WordPair pair) {
final alreadySaved = _saved.contains(pair);
return Column(
children: <Widget>[
ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
),
trailing: Icon(
alreadySaved ? Icons.check : Icons.check,
color: alreadySaved ? Colors.deepOrange : null,
),
onTap: () {
setState(() {
if (alreadySaved) {
_saved.remove(pair);
} else {
_saved.add(pair);
}
});
},
),
const Divider(),
],
);
}
Widget _buildSuggestions() {
return ListView.builder(
reverse: true,
padding: const EdgeInsets.all(16.0),
controller: controller,
itemCount: _all.length,
itemBuilder: (context, i) {
return _buildRow(_all[i]);
});
}
Widget _loader() {
return isLoading
? const Align(
child: SizedBox(
width: 70.0,
height: 70.0,
child: Padding(
padding: EdgeInsets.all(5.0),
child: Center(child: CircularProgressIndicator())),
),
alignment: FractionalOffset.topCenter,
)
: const SizedBox(
width: 0.0,
height: 0.0,
);
}
}
You can get full code from Github HERE
I have wrapped the body of the code below using GestureDetector, thus enabling me to use onVerticalDragEnd method available in the widget. When the app detects a Vertical Drag the _onRefreshing function is called, where it updates the Text widget after a delay of 2 seconds.
I want to include a Loading indicator while the _onRefreshing function is running.
How do I implement this task in Flutter?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
dynamic balanceAvailable = 0;
void _onRefreshing(DragEndDetails details) async {
await Future.delayed(const Duration(seconds: 2));
if (details.velocity.pixelsPerSecond.dy > 0) {
setState(() {
balanceAvailable = 1000;
});
print('newbalance : $balanceAvailable');
print(details.velocity.pixelsPerSecond.dy);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: GestureDetector(
onVerticalDragEnd: _onRefreshing,
child: Container(
width: double.infinity,
color: Colors.lightBlue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: () {},
child: Text("Button 1"),
),
SizedBox(height: 100.0),
Text('$balanceAvailable'),
],
),
),
),
),
);
}
}
You can return a CircularProgressIndicator inside a showdialog in your _onRefreshing method.
After the 2 seconds delay, you can remove it with Navigator.pop();
Maybe like this:
void _onRefreshing(DragEndDetails details) async {
showDialog(
context: context,
builder: (BuildContext context) {
return Center(
child: SizedBox(
height: MediaQuery.of(context).size.height/4,
width: MediaQuery.of(context).size.width/2,
child: CircularProgressIndicator(
valueColor: new AlwaysStoppedAnimation<Color>(Colors.red),
),
),
);
});
await Future.delayed(const Duration(seconds: 2));
if (details.velocity.pixelsPerSecond.dy > 0) {
setState(() {
balanceAvailable = 1000;
});
print('newbalance : $balanceAvailable');
print(details.velocity.pixelsPerSecond.dy);
}
Navigator.pop(context);
}
I have an Apps which is having a listview with the reaction button in a flutter . I want to make this when a user clicked any of this love icon then it's filled with red color.
enter image description here
enter image description here
Like this image but the problem is when I clicked one of this love icon all of the icons turned into red color but I only want to change the color of love of icon which one is Selected.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool like;
#override
List<String> user = ['Dipto', 'Dipankar', "Sajib", 'Shanto', 'Pranto'];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView Demu'),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: user.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(10),
height: 50,
width: MediaQuery.of(context).size.width * 0.8,
color: Colors.yellowAccent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
user[index],
),
Positioned(
child: IconButton(
icon: _iconControl(like),
onPressed: () {
if (like == false) {
setState(() {
like = true;
_iconControl(like);
});
} else {
setState(() {
like = false;
_iconControl(like);
});
}
},
),
),
],
),
);
},
),
)),
);
}
_iconControl(bool like) {
if (like == false) {
return Icon(Icons.favorite_border);
} else {
return Icon(
Icons.favorite,
color: Colors.red,
);
}
}
}
I also try with using parameter but Its failed Like that :
child: IconButton(
icon: _iconControl(true),
onPressed: () {
if (false) {
setState(() {
_iconControl(true);
});
} else {
setState(() {
_iconControl(false);
});
}
},
),
Can you help me Please. Thanks in advance
You can create a modal class to manage the selection of your list
Just create a modal class and add a boolean variable to maintaining selection using. that boolean variable
SAMPLE CODE
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool like;
List<Modal> userList = List<Modal>();
#override
void initState() {
userList.add(Modal(name: 'Dipto', isSelected: false));
userList.add(Modal(name: 'Dipankar', isSelected: false));
userList.add(Modal(name: 'Sajib', isSelected: false));
userList.add(Modal(name: 'Shanto', isSelected: false));
userList.add(Modal(name: 'Pranto', isSelected: false));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView Demu'),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: userList.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(10),
height: 50,
width: MediaQuery
.of(context)
.size
.width * 0.8,
color: Colors.yellowAccent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
userList[index].name,
),
Positioned(
child: IconButton(
icon: _iconControl( userList[index].isSelected),
onPressed: () {
setState(() {
userList.forEach((element) {
element.isSelected = false;
});
userList[index].isSelected = true;
});
},
),
),
],
),
);
},
),
)),
);
}
_iconControl(bool like) {
if (like == false) {
return Icon(Icons.favorite_border);
} else {
return Icon(
Icons.favorite,
color: Colors.red,
);
}
}
}
class Modal {
String name;
bool isSelected;
Modal({this.name, this.isSelected = false});
}
I have created a Custom Segments widget which creates Multiple TABS according to List. I am updating selectionsList from homepage.dart but still, my segments are not updating runtime according to changed selectionsList
Segments.dart (Custom SegmentWidget which creates Cupertino tabs)
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SegmentsWidget extends StatefulWidget {
#override
_SegmentsWidgetState createState() => _SegmentsWidgetState();
final List selectionsList;
final ValueChanged<int> onSelectTab;
final VoidCallback onTap;
final int selectedValue;
SegmentsWidget(
{this.selectionsList, this.onSelectTab, this.onTap, this.selectedValue});
}
class _SegmentsWidgetState extends State<SegmentsWidget> {
Map<int, Widget> tabWidget = Map<int, Widget>();
int selectedTab = 0;
#override
void initState() {
super.initState();
print("INit State ${widget.selectionsList}");
setState(() {
widget.selectionsList.asMap().forEach((index, value) {
tabWidget.addAll({
index: Container(
height: 40,
child: Center(
child: Text(
widget.selectionsList[index],
style: TextStyle(fontFamily: 'Exo2', fontSize: 12.0),
),
))
});
});
});
}
#override
void didUpdateWidget(SegmentsWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("Did update");
}
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Expanded(
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: CupertinoSegmentedControl<int>(
padding: EdgeInsets.symmetric(vertical: 8),
children: tabWidget,
onValueChanged: (int index) {
setState(() {
selectedTab = index;
});
widget.onSelectTab(index);
},
groupValue: widget.selectedValue ?? selectedTab,
),
),
)
],
),
);
}
}
HomePage.dart
From Home Page, I am updating selection array but still my segments are not updating according to selectionList.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> selection;
int selectedTab = -1;
#override
void initState() {
super.initState();
selection = ["A", "B"];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Dynamic Segments")),
body: Container(
child: Column(
children: <Widget>[
SegmentsWidget(
selectionsList: selection,
onSelectTab: (selectTab) {
setState(() {
selectedTab = selectTab;
});
},
),
RaisedButton(child: Text("AB"),onPressed: (){
setState(() {
selection = ["A", "B"];
});
}),
RaisedButton(child: Text("ABC"),onPressed: (){
setState(() {
selection = ["A", "B", "C"];
});
}),
RaisedButton(child: Text("ABCD"),onPressed: (){
setState(() {
selection = ["A", "B", "C","D"];
});
})
],
),
),
);
}
}
I assume that initState of Segment Widget called once only. I even tried in didUpdateWidget but still not getting updated tabs.
Issue: How to update tabWidgets which is mentioned in my custom widget from another stateful widget?
I change some parts of code.
Instead of calling your code (that need to be called again on setState) in initState() function, call your code inside the widget with your own method.
see getTabChilds() function below of code.
class _SegmentsWidgetState extends State<SegmentsWidget> {
Map<int, Widget> tabWidget = Map<int, Widget>();
int selectedTab = 0;
#override
void initState() {
super.initState();
print("INit State ${widget.selectionsList}");
}
#override
void didUpdateWidget(SegmentsWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print("Did update");
}
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: <Widget>[
Expanded(
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: CupertinoSegmentedControl<int>(
padding: EdgeInsets.symmetric(vertical: 8),
children: getTabChilds(),
onValueChanged: (int index) {
setState(() {
selectedTab = index;
});
widget.onSelectTab(index);
},
groupValue: widget.selectedValue ?? selectedTab,
),
),
)
],
),
);
}
Map<int, Widget> getTabChilds() {
tabWidget = Map<int, Widget>();
widget.selectionsList.asMap().forEach((index, value) {
tabWidget.addAll({
index: Container(
height: 40,
child: Center(
child: Text(
widget.selectionsList[index],
style: TextStyle(fontFamily: 'Exo2', fontSize: 12.0),
),
))
});
});
return tabWidget;
}
}
It's tested and works fine.