At the end of liquid swipe, when the Let's start button is clicked, I would like confetti animation to play for 3-4 seconds and then present the user with App home screen. When I run this code, I don't get any error and neither does confetti animation play. On clicking Let's start, the home screen of app is displayed.
Update: 25/11/2022 I was able to run confetti successfully and have also added a listener to capture current state. I would like the confetti to run for 3 seconds and the screen to be routed to home screen using
AppRoute.pushReplacement(context, const MyApp());
import 'package:a/main.dart';
import 'package:a/provider/home_provider.dart';
import 'package:a/provider/user_provider.dart';
import 'package:a/ui/app_route.dart';
import 'package:a/ui/widget/styled_text.dart';
import 'package:a/utils/app_colors.dart';
import 'package:flutter/material.dart';
import 'package:liquid_swipe/liquid_swipe.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:confetti/confetti.dart';
class OnBoarding extends StatefulWidget {
const OnBoarding({Key? key}) : super(key: key);
#override
_OnBoardingState createState() => _OnBoardingState();
}
class _OnBoardingState extends State<OnBoarding> {
#override
Widget build(BuildContext context) {
final pages = [
IntroductionContainer(
heading:
'a',
body:
"b",
color: Color(0xff3937bf),
animation: 'assets/lottie/a.json',
),
IntroductionContainer(
heading:
'c?',
body:
"d",
color: Colors.pink,
animation: 'assets/lottie/h.json',
),
IntroductionContainer(
heading:
'e',
body:
"f",
color: Color(0xff27b56f),
animation: 'assets/lottie/g.json',
),
IntroductionContainer(
showAction: true,
heading: 'Ready ?',
body:
"aaaa",
color: Color(0xfff46d37),
),
];
return Scaffold(
body: LiquidSwipe(
pages: pages,
enableSideReveal: true,
// enableSlideIcon: true,
enableLoop: false,
positionSlideIcon: 0,
slideIconWidget: const Icon(
Icons.arrow_back_ios,
size: 30,
color: Colors.white,
),
),
);
}
}
class IntroductionContainer extends StatefulWidget {
IntroductionContainer(
{required this.heading,
required this.body,
required this.color,
this.showAction = false,
this.animation,
Key? key})
: super(key: key);
final String heading;
final String body;
final Color color;
final bool showAction;
final String? animation;
#override
State<IntroductionContainer> createState() => _IntroductionContainerState();
}
class _IntroductionContainerState extends State<IntroductionContainer> {
//Adding Confetti controller and other variables
bool isPlaying = false;
final _controller = ConfettiController(duration: const Duration(seconds: 2));
#override
void initState() {
// TODO: implement initState
super.initState();
//Listen to states playing, stopped
_controller.addListener(() {
setState(() {
isPlaying = _controller.state == ConfettiControllerState.playing;
});
if (_controller.state == ConfettiControllerState.stopped) {
AppRoute.pushReplacement(context, const MyApp());
}
});
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
_controller.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
widget.heading,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 25,
),
),
const SizedBox(height: 10),
if (widget.animation != null)
Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Lottie.asset(
widget.animation!,
height: 400,
width: 400,
fit: BoxFit.fill,
),
),
if (widget.animation == null)
InkWell(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: Stack(
alignment: Alignment.center,
children: [
Container(
height: 200,
width: 200,
alignment: Alignment.center,
child: Image.asset(
'assets/a-2.png',
height: 120,
width: 120,
fit: BoxFit.contain,
),
),
const Icon(
Icons.play_circle,
size: 60,
)
],
),
),
onTap: () async {
var url = 'https://www.youtube.com/watch?v=aaaaaa';
if (await canLaunchUrlString(url)) {
launchUrlString(url);
}
},
),
Text(
widget.body,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
),
),
ConfettiWidget(
confettiController: _controller,
//set direction
//blastDirectionality: BlastDirectionality.explosive,
// to set direction of confetti upwards
blastDirection: -3.14 / 2,
//minBlastForce: 10,
//maxBlastForce: 100,
//colors: const [Colors.deepPurple, Colors.black, Colors.yellow],
numberOfParticles: 20,
gravity: 0.5,
emissionFrequency: 0.3,
// - Path to create oval particles
createParticlePath: (size) {
final path = Path();
path.addOval(Rect.fromCircle(
center: Offset.zero,
radius: 4,
));
return path;
},
// Path to create star confetti
/// A custom Path to paint stars.
//Path drawStar(Size size) {
// Method to convert degree to radians
//double degToRad(double deg) => deg * (pi / 180.0);
//const numberOfPoints = 5;
//final halfWidth = size.width / 2;
//final externalRadius = halfWidth;
//final internalRadius = halfWidth / 2.5;
//final degreesPerStep = degToRad(360 / numberOfPoints);
//final halfDegreesPerStep = degreesPerStep / 2;
//final path = Path();
//final fullAngle = degToRad(360);
//path.moveTo(size.width, halfWidth);
//for (double step = 0; step < fullAngle; step += degreesPerStep) {
//path.lineTo(halfWidth + externalRadius * cos(step),
// halfWidth + externalRadius * sin(step));
//path.lineTo(halfWidth + internalRadius * cos(step + halfDegreesPerStep),
// halfWidth + internalRadius * sin(step + halfDegreesPerStep));
// }
//path.close();
//return path;
//}
),
//ConfettiWidget(
const SizedBox(height: 50),
if (widget.showAction)
ButtonTheme(
height: 50,
minWidth: 150,
child: MaterialButton(
// borderSide: BorderSide(color: Colors.white),
color: AppColors.blue,
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: const Text(
'Let\'s Go',
style: TextStyle(fontSize: 20),
),
onPressed: () async {
//Calling Confetti controller and adding confetti widget
if (isPlaying) {
_controller.stop();
//await Duration(seconds: 4);
AppRoute.pushReplacement(context, const MyApp());
} else {
_controller.play();
//await Duration(seconds: 1);
}
//await Duration(seconds: 1);
//confettiController: confettiController,
// shouldLoop: false,
// blastDirectionality: BlastDirectionality.explosive,
//);
//var userProvider =
//Provider.of<UserProvider>(context, listen: false);
//userProvider.updateTrial();
// HomeProvider.setAppLaunched();
//AppRoute.pushReplacement(context, const MyApp());
//},
}),
),
],
),
),
);
}
}
At the end of liquid swipe, when the Let's start button is clicked, I would like confetti animation to play for 3-4 seconds and then present the user with App home screen. When I run this code, I don't get any error and neither does confetti animation play. On clicking Let's start, the home screen of app is displayed.
PS: I am new to flutter
Your mistake is that you put the ConfettiWidget inside of the onPressed callback of your button.
You should add it in your Column like any other widget and display the confetti by triggering the controller you pass to your ConfettiWidget inside the onPressed callback with the help of _controller.play()
Related
I've been trying to implement a similar function like the NavigationBar widget in flutter.
However, I don't want to use Icons instead I wanted to make a custom navbar with desired pics and everything was going well until I couldn't switch the middle section of my app (change different pages) when I tap/press the the textbutton.
You can check the UI here...crappy I know...am mimicking the till from my workplace...so the red section is the part I wanted to update when pressed
The side_buttons.dart file
import 'package:flutter/material.dart';
// ignore: unused_import
import 'package:timtill/main.dart';
class SideButtons extends StatefulWidget {
final String text;
final String imgUrl;
const SideButtons({required this.text, required this.imgUrl});
#override
State<SideButtons> createState() => SideButtonsState();
}
class SideButtonsState extends State<SideButtons> {
//
final List sideBtnLabels = [
'HOT DRINKS',
'COLD DRINKS',
'DONUTS',
'TIMBITS',
'MUFFINS',
'BAGELS',
'SOUP',
'LUNCH',
'BREAK FAST',
'BAKED',
'TAKE-HOME',
'Timmies'
];
#override
Widget build(BuildContext context) {
return Transform.rotate(
angle: -11,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF9A9DAD), Color(0xFF4E4C56)])),
height: 80,
width: 80,
child: TextButton(
onPressed: () {
int currentPageIndex = 0;
int index = sideBtnLabels.indexOf(widget.text);
setState(() {
currentPageIndex = index;
});
int navMiddleIndex(int index) {
return index;
}
print(sideBtnLabels.indexOf(widget.text));
// print('index is changed to: ${navMiddleIndex(index).toString()}');
},
//////here Instead of text you can replace Node and import the dart:html
//import 'dart:html';
// text works because in the side_btn_page.dart we have specified the list of menu to it
child: Stack(
alignment: const AlignmentDirectional(0.0, 0.9),
children: [
Image.asset(
'imgs/' + widget.imgUrl,
//imgurl
),
Text(
widget.text, //text
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = const Color.fromARGB(255, 63, 63, 63),
),
),
Text(
widget.text, //text
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFEBEBEB),
),
),
],
)),
),
);
}
}
'''
The Main.dart file
Note I wanted to update the currentPageIndex value from zero to the index number When I press the buttons please help me I'm beginner
import 'package:flutter/material.dart';
import 'package:timtill/pages/side_btn_page.dart';
import 'package:timtill/pages/middle_btn_page.dart';
import 'package:timtill/pages/middle_btn_page2.dart';
// ignore: unused_import
import 'package:timtill/util/side_buttons.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'TimsTill',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int currentPageIndex = 0;
#override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
children: [
SizedBox(height: 80, child: SideButtonPage()),
Expanded(
flex: 12,
child: Container(
color: Colors.red,
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.all(8),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: <Widget>[
MiddleButtonPage(),
MiddleButtonPage2(),
Container(
color: Colors.green,
alignment: Alignment.center,
child: const Text('Page 2'),
),
Container(
color: Colors.blue,
alignment: Alignment.center,
child: const Text('Page 3'),
),
][currentPageIndex],
),
),
),
),
)),
Expanded(
flex: 6,
child: Container(
color: Colors.purple,
))
],
),
);
}
}
First of all, you should implement a callback in your SideButtons widget, second, you should implement the defaultPageIndex. This way, SideButtons will return the selected index to its parent widget while maintening its state incase the widget try is rebuilt.
class SideButtons extends StatefulWidget {
final String text;
final String imgUrl;
final int defaultPageIndex;
final ValueChanged<int>? onChanged;
const SideButtons({required this.text, required this.imgUrl, this.defaultPageIndex = 0, this.onChanged});
#override
State<SideButtons> createState() => SideButtonsState();
}
class SideButtonsState extends State<SideButtons> {
//
final List sideBtnLabels = [
'HOT DRINKS',
'COLD DRINKS',
'DONUTS',
'TIMBITS',
'MUFFINS',
'BAGELS',
'SOUP',
'LUNCH',
'BREAK FAST',
'BAKED',
'TAKE-HOME',
'Timmies'
];
late int currentPageIndex;
#override
initState(){
currentPageIndex = defaultPageIndex;
super.initState();
}
#override
Widget build(BuildContext context) {
return Transform.rotate(
angle: -11,
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFF9A9DAD), Color(0xFF4E4C56)])),
height: 80,
width: 80,
child: TextButton(
onPressed: () {
int index = sideBtnLabels.indexOf(widget.text);
setState(() {
currentPageIndex = index;
if( widget.onChanged != null) widget.onChanged(index);
});
int navMiddleIndex(int index) {
return index;
}
print(sideBtnLabels.indexOf(widget.text));
// print('index is changed to: ${navMiddleIndex(index).toString()}');
},
//////here Instead of text you can replace Node and import the dart:html
//import 'dart:html';
// text works because in the side_btn_page.dart we have specified the list of menu to it
child: Stack(
alignment: const AlignmentDirectional(0.0, 0.9),
children: [
Image.asset(
'imgs/' + widget.imgUrl,
//imgurl
),
Text(
widget.text, //text
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 3
..color = const Color.fromARGB(255, 63, 63, 63),
),
),
Text(
widget.text, //text
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Color(0xFFEBEBEB),
),
),
],
)),
),
);
}
}
I have made radius based flutter google maps in which I am using circle property to draw a circle radius based. It only shows up when I increment or decrement the radius but I want it to be showing by default as well when user navigates to location screen in my flutter app. This is how the map screen loads and as soon as i tap the + or - buttons it will start showing the circle like this I don't know why this is happening. If anyone knows what am I doing wrong then please let me know. I am attaching the code of the screen below :
class MapRadius extends StatefulWidget {
const MapRadius({Key? key}) : super(key: key);
#override
State<MapRadius> createState() => _MapRadiusState();
}
class _MapRadiusState extends State<MapRadius> {
// List<Marker> myMarker = [];
final Set<Circle> circle = {};
// late GoogleMapController mapController;
final Completer<GoogleMapController> _controller = Completer();
int _n = 5;
// LatLng startLoc = const LatLng(52.0907374, 5.1214201);
LatLng? currentLatLng;
late GoogleMapController mapController;
Location location = Location();
var latitude;
var longitude;
late LocationData _locationData;
get() async {
_locationData = await location.getLocation();
latitude = _locationData.latitude;
longitude = _locationData.longitude;
setState(() {
currentLatLng = LatLng(latitude, longitude);
});
}
#override
initState() {
super.initState();
get();
}
#override
void setState(VoidCallback fn) {
super.setState(fn);
FirebaseFirestore.instance
.collection("userpreferences")
.doc(FirebaseAuth.instance.currentUser!.uid)
.update({"radius": _n});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: const Color(0xFF2A3B6A),
title: const DelayedDisplay(
delay: Duration(seconds: 1), child: Text('Location Range')),
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.of(context).pop();
},
),
actions: const [
Padding(
padding: EdgeInsets.only(right: 20),
child: Tooltip(
showDuration: Duration(seconds: 5),
triggerMode: TooltipTriggerMode.tap,
textStyle: TextStyle(
fontSize: 18, color: Colors.white, fontFamily: "ProductSans"),
message:
'Increase or decrease radius according to your own preference. + and - can be used to add Kilometers.\nThe Kilometers are multiples of 10 (e.g. 5 = 50KM)\nMax limit 100 KM nearby. Default is 50KM.\nRange gets updated as soon as you add or remove a kilometer.',
child: Icon(Icons.info),
),
),
],
),
body: currentLatLng == null
? Center(
child: SpinKitSpinningLines(
color: Theme.of(context).primaryColor,
size: 90.0,
lineWidth: 5,
))
: Stack(children: <Widget>[
GoogleMap(
circles: circle,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
initialCameraPosition:
CameraPosition(target: currentLatLng!, zoom: 12),
),
]),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 40.0),
child: Row(
children: <Widget>[
SizedBox(
width: 40.0,
height: 40.0,
child: FloatingActionButton(
heroTag: "btnAdd",
onPressed: add,
child: LineIcon(
LineIcons.plus,
color: Colors.black,
size: 30,
),
backgroundColor: Colors.white,
),
),
const SizedBox(
width: 5,
),
Text('$_n', style: const TextStyle(fontSize: 26.0)),
const SizedBox(
width: 5,
),
SizedBox(
width: 40.0,
height: 40.0,
child: FloatingActionButton(
heroTag: "btnMinus",
onPressed: minus,
child: LineIcon(
LineIcons.minus,
color: Colors.black,
size: 30,
),
backgroundColor: Colors.white,
),
),
],
),
),
],
),
);
}
void add() {
setState(() {
if (_n < 10) {
_n++;
}
addRadiusToMap(_n);
});
}
void minus() {
if (_n != 1) {
setState(() {
_n--;
addRadiusToMap(_n);
});
}
}
void addRadiusToMap(radius) {
double reciprocal(double d) => 700 * d; // 1000 before
circle.clear();
circle.add(Circle(
circleId: const CircleId("1"),
center: currentLatLng!,
radius: reciprocal(radius.toDouble()),
));
}
}
Auto filling of OTP is happening,but unable to verify when auto filling is used where as I am able to verify when OTP is entered manually
Here is my code for OTP autofilling
import 'package:flutter/material.dart';
import 'package:sms_autofill/sms_autofill.dart';
import 'package:flutter/material.dart';
import 'package:mvc_pattern/mvc_pattern.dart';
import 'package:otp_text_field/otp_field.dart';
import 'package:otp_text_field/style.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:sms_autofill/sms_autofill.dart';
import 'package:multisuperstore/src/controllers/user_controller.dart';
import 'dart:math' as math;
import 'package:multisuperstore/generated/l10n.dart';
class VerifyOTPScreen extends StatefulWidget {
String verificationId;
String mobileNo;
VerifyOTPScreen({
this.verificationId,
this.mobileNo,
Key key,
}) : super(key: key);
#override
State<VerifyOTPScreen> createState() => _VerifyOTPScreenState();
}
class _VerifyOTPScreenState extends State<VerifyOTPScreen> with CodeAutoFill {
String codeValue = "";
#override
void codeUpdated() {
print("Update code $code");
setState(() {
print("codeUpdated");
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
listenOtp();
}
void listenOtp() async {
// await SmsAutoFill().unregisterListener();
// listenForCode();
await SmsAutoFill().listenForCode;
print("OTP listen Called");
}
#override
void dispose() {
SmsAutoFill().unregisterListener();
print("unregisterListener");
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(30.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Center(
child: PinFieldAutoFill(
currentCode: codeValue,
codeLength: 4,
onCodeChanged: (code) {
print("onCodeChanged $code");
setState(() {
codeValue = code.toString();
});
},
onCodeSubmitted: (val) {
print("onCodeSubmitted $val");
},
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
print(codeValue);
},
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
textStyle: MaterialStateProperty.all(TextStyle(fontSize: 25))),
child: const Text("Verify OTP"), ),
const SizedBox(
height: 20,
),
ElevatedButton(onPressed: listenOtp,style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.blue),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
textStyle: MaterialStateProperty.all(TextStyle(fontSize: 25))), child: const Text("Resend"))
],
),
),
);
}
}
Here is the code for manually entering OTP and verifying please suggest the changes
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:multisuperstore/src/controllers/user_controller.dart';
import 'package:mvc_pattern/mvc_pattern.dart';
import 'dart:math' as math;
import 'package:otp_text_field/otp_field.dart';
import 'package:otp_text_field/style.dart';
import '../../generated/l10n.dart';
import 'package:sms_autofill/sms_autofill.dart';
// ignore: must_be_immutable
class OtpVerificationSmart extends StatefulWidget {
String verificationId;
String mobileNo;
OtpVerificationSmart({
Key key,
this.verificationId,
this.mobileNo,
}) : super(key: key);
#override
_OtpVerificationSmartState createState() => _OtpVerificationSmartState();
}
class _OtpVerificationSmartState extends StateMVC<OtpVerificationSmart> with TickerProviderStateMixin {
AnimationController controller1;
final TextEditingController _controllerOTPPhone = TextEditingController();
bool isPlaying = true;
UserController _con;
_OtpVerificationSmartState() : super(UserController()) {
_con = controller;
}
String get timerString {
Duration duration = controller1.duration * controller1.value;
String timer = '${duration.inMinutes}:${(duration.inSeconds % 60).toString().padLeft(2, '0')}';
if (timer == '0:00') {
isPlaying = false;
}
return '${duration.inMinutes}:${(duration.inSeconds % 300).toString().padLeft(2, '0')}';
}
#override
void initState() {
super.initState();
controller1 = AnimationController(
vsync: this,
duration: Duration(seconds: 60),
);
controller1.reverse(from: controller1.value == 0.0 ? 1.0 : controller1.value);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
automaticallyImplyLeading: true,
centerTitle: true,
title: Text('OTP ${S.of(context).verification}',style: Theme.of(context).textTheme.headline1,),
),
key: _con.scaffoldKey,
body: Padding(
padding: EdgeInsets.all(8.0),
child: Form(
key: _con.loginFormKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Align(
alignment: FractionalOffset.center,
child:Image.asset('assets/img/cart_empty_ipmau1.png',
width: 150,height:150,fit: BoxFit.fill,
)
),
),
Text(S.of(context).verification_code, style: Theme.of(context).textTheme.headline1.merge(TextStyle(fontWeight: FontWeight.bold))),
SizedBox(height: 10.0),
Text("${S.of(context).we_have_sent_the_code_verification_to_your_mobile_number} ${widget.mobileNo}",
style: Theme.of(context).textTheme.caption,
textAlign: TextAlign.center, ),
SizedBox(height: 20),
Container(
padding: EdgeInsets.only(top:20,left: 20, right: 20),
child: SingleChildScrollView(
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: OTPTextField(
length: 6,
width: MediaQuery.of(context).size.width,
textFieldAlignment: MainAxisAlignment.spaceAround,
fieldWidth: 40,
style: TextStyle(fontSize: 20),
fieldStyle: FieldStyle.underline,
onCompleted: (pin) {
_controllerOTPPhone.text = pin;
},
),
),
],
),
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.04,
),
),
),
SizedBox(height: 43.0),
Container(
margin: EdgeInsets.only(left:20,right:20),
// ignore: deprecated_member_use
child:RaisedButton(
onPressed: () {
// Navigator.of(context).pushNamed('/SignUp', );
PhoneAuthCredential phoneAuthCredential =
PhoneAuthProvider.credential(
verificationId: widget.verificationId, smsCode: _controllerOTPPhone.text);
_con.signInWithPhoneAuthCredential(phoneAuthCredential);
},
shape: StadiumBorder(),
padding: EdgeInsets.all(0.0),
child: Ink(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(30.0)),
child: Container(
constraints: BoxConstraints(maxWidth: double.infinity, minHeight: 50.0),
alignment: Alignment.center,
child: Text(S.of(context).verify,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headline4.merge(TextStyle(color: Theme.of(context).primaryColorLight))),
),
),
),),
SizedBox(height: 80.0),
],
),
),
),
);
}
}
class TimerPainter extends CustomPainter {
TimerPainter({
this.animation,
this.backgroundColor,
this.color,
}) : super(repaint: animation);
final Animation<double> animation;
final Color backgroundColor, color;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = backgroundColor
..strokeWidth = 3.0
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;
canvas.drawCircle(size.center(Offset.zero), size.width / 2.0, paint);
paint.color = color;
double progress = (1.0 - animation.value) * 2 * math.pi;
canvas.drawArc(Offset.zero & size, math.pi * 1.5, -progress, false, paint);
}
#override
bool shouldRepaint(TimerPainter old) {
return animation.value != old.animation.value || color != old.color || backgroundColor != old.backgroundColor;
}
}
Please help me with the code under onPressed event for Verify OTP button
i am trying to check if the user in specific geofencing zone using the Easy geofencing package but every time i call the EasyGeofencing.startGeofenceService() and EasyGeofencing.getGeofenceStream() with new position ( pointedLatitude, pointedLongitude, radiusMeter) the service do not work and the console print "Parse value===> false" if any one know how i solve this problem please help me, i am stuck since a week.
this is my code
import 'dart:async';
import 'package:easy_geofencing/easy_geofencing.dart';
import 'package:geolocator/geolocator.dart';
import 'package:easy_geofencing/enums/geofence_status.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/domain/models/shop_model.dart';
import 'package:flutter_application_1/presentation/resources/assets_manager.dart';
import 'package:flutter_application_1/presentation/resources/color_manager.dart';
import 'package:flutter_application_1/presentation/resources/values_manager.dart';
import 'package:flutter_application_1/presentation/sidebars/cardWidget.dart';
import 'package:flutter_application_1/presentation/sidebars/profileSideBar.dart';
import 'package:flutter_application_1/services/shop_services.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
class ShopsDownBar extends StatefulWidget with ChangeNotifier {
ShopsDownBar({Key? key}) : super(key: key);
#override
State<ShopsDownBar> createState() => _ShopsDownBarState();
}
class _ShopsDownBarState extends State<ShopsDownBar>
with SingleTickerProviderStateMixin<ShopsDownBar> {
StreamSubscription<GeofenceStatus>? geofenceStatusStream;
StreamSubscription<EasyGeofencing>? easyGeofencingStream;
Geolocator geolocator = Geolocator();
String geofenceStatus = '';
bool isReady = false;
int raduis = 25;
double? longitude;
double? latitude;
Position? position;
late StreamController<bool> isOpenStreamController;
late Stream<bool> isOpenNStream;
late StreamSink<bool> isOpenNSink;
late AnimationController _animationControler;
List<Shops> shops = [];
getCurrentPosition() async {
position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
print("LOCATION => ${position?.toJson()}");
isReady = (position != null) ? true : false;
}
setLocation() async {
await getCurrentPosition();
print("POSITION => ${position!.toJson()}");
}
final _animationDuration = const Duration(milliseconds: 500);
setShops() {
ShopServices().fetchShops().then((value) {
setState(() {
if (value != null) {
for (int i = 0; i < value.length; i++) {
shops.add(Shops(
type: value[i].type,
shopStatus: value[i].shopStatus,
rewards: value[i].rewards,
id: value[i].id,
shopName: value[i].shopName,
shopAddress: value[i].shopAddress,
markerShop: value[i].markerShop,
createdAt: value[i].createdAt,
updatedAt: value[i].updatedAt,
));
}
}
});
});
}
void onIconPressed() {
final animationStatus = _animationControler.status;
final isAnimationDone = animationStatus == AnimationStatus.completed;
if (isAnimationDone) {
isOpenNSink.add(false);
_animationControler.reverse();
} else if (Provider.of<ProfileSideBar>(context, listen: false).isOpen ==
false) {
isOpenNSink.add(true);
_animationControler.forward();
}
}
#override
void initState() {
setShops();
getCurrentPosition();
if (isReady) {
print('jawna behi');
setState(() {
setLocation();
});
}
_animationControler =
AnimationController(vsync: this, duration: _animationDuration);
isOpenStreamController = PublishSubject<bool>();
isOpenNStream = isOpenStreamController.stream;
isOpenNSink = isOpenStreamController.sink;
super.initState();
}
#override
void dispose() {
_animationControler.dispose();
isOpenStreamController.close();
isOpenNSink.close();
EasyGeofencing.stopGeofenceService();
super.dispose();
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
var height = size.height;
var width = size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isOpenNStream,
builder: (context, isOpenAsync) {
return AnimatedPositioned(
duration: _animationDuration,
top: isOpenAsync.data == false ? height * 0.91 : height * 0.24,
bottom: AppSize.s1_5,
right: AppSize.s1_5,
left: AppSize.s1_5,
child: Column(
children: [
Align(
child: GestureDetector(
onTap: () {
onIconPressed();
},
child: Container(
// alignment: Alignment.cen,
padding: const EdgeInsets.only(
left: AppMargin.m60,
right: AppMargin.m60,
top: AppMargin.m8,
),
child: Icon(
isOpenAsync.data == true
? Icons.close
: Icons.wallet_giftcard,
size: AppSize.s28,
color: ColorManager.primary,
),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(AppSize.s40),
topRight: Radius.circular(AppSize.s40)),
),
),
),
),
Expanded(
child: Container(
margin: const EdgeInsets.only(
left: AppMargin.m16, right: AppMargin.m16),
height: height / 1.4,
width: width,
decoration: BoxDecoration(
color: ColorManager.white,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(AppSize.s28),
topRight: Radius.circular(AppSize.s28)),
),
child: shops.isNotEmpty
? ListView.builder(
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(
bottom: AppPadding.p8,
top: AppPadding.p18),
child: Giftcart(
ontap: () {
// print("starting geoFencing Service");
EasyGeofencing.startGeofenceService(
pointedLatitude: shops[index]
.markerShop
.locations
.latitude
.toString(),
pointedLongitude: shops[index]
.markerShop
.locations
.longitude
.toString(),
radiusMeter: raduis.toString(),
eventPeriodInSeconds: 5);
geofenceStatusStream ??=
EasyGeofencing.getGeofenceStream()!
.listen((GeofenceStatus? status) {
print(status.toString());
setState(() {
geofenceStatus = status.toString();
});
if (status.toString() ==
'GeofenceStatus.enter') {
print("entered");
} else {
print("not entered");
}
});
},
shopName: shops[index].shopName,
details: shops[index].shopAddress,
imagePath: ImageAssets.logo1,
),
);
},
itemCount: shops.length)
: Padding(
padding: const EdgeInsets.fromLTRB(
AppPadding.p100,
AppPadding.p100,
AppPadding.p100,
AppPadding.p200),
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
ColorManager.primary),
),
)),
),
],
),
);
});
}
}
giftCart widget
import 'package:flutter/material.dart';
import 'package:flutter_application_1/presentation/resources/color_manager.dart';
import 'package:flutter_application_1/presentation/resources/values_manager.dart';
//import 'package:flutter_application_1/presentation/resources/font_manager.dart';
class Giftcart extends StatelessWidget {
final String shopName;
final String details;
final String imagePath;
final void Function() ontap;
const Giftcart({
Key? key,
required this.ontap,
required this.shopName,
required this.details,
required this.imagePath,
}) : super(key: key);
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
var height = size.height;
var width = size.width;
return Center(
child: Container(
decoration: BoxDecoration(
color: ColorManager.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: ColorManager.grey,
offset: const Offset(0, 0),
blurRadius: 10,
),
],
),
height: height * 0.1,
width: width * 0.8,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: height * 0.15,
width: width * 0.15,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
),
child: Image.asset(imagePath)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: height * 0.02,
),
Text(
shopName,
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.end,
),
SizedBox(
height: height * 0.01,
),
Text(
details,
style: Theme.of(context).textTheme.bodyText1,
textAlign: TextAlign.start,
),
],
),
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: SizedBox(
height: height * 0.12,
width: width * 0.12,
child: FloatingActionButton(
backgroundColor: ColorManager.primary,
onPressed: ontap,
child: Icon(
Icons.card_giftcard,
size: AppSize.s18,
color: ColorManager.white,
)),
),
),
],
),
),
);
}
}
the Screen
the button on the left is for checking the geofenceStatus
this is the package doc
Add "geofenceStatusStream = null;" after stopGeofenceService as below:
setState(() {
EasyGeofencing.stopGeofenceService();
geofenceStatusStream!.cancel();
geofenceStatusStream = null;
});
I'm using Flutter 2.5.1 and I'm having this error:
"text_painter.dart: Failed assertion: line 900 pos 12: '!_needsLayout': is not true"
while using the following code (which generates a switch button, just like IOS, in Flutter). I found it here: pub.dev, and I'm modifying it in order to add null safety. However, I have encountered a bug that I do not understand.
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math';
/// Customable and attractive Switch button.
/// Currently, you can't change the widget
/// width and height properties.
///
/// As well as the classical Switch Widget
/// from flutter material, the following
/// arguments are required:
///
/// * [value] determines whether this switch is on or off.
/// * [onChanged] is called when the user toggles the switch on or off.
class LiteRollingSwitch extends StatefulWidget {
/// A required boolean that sets the value of the button.
/// * "on" = `true`
/// * "off" = `false`
final bool value;
/// A function called every time there is a change in state. (required)
final Function(bool) onChanged;
/// Text displayed when the [value] is `false`. By default
/// * "Off"
final String textOff;
/// Text displayed when the [value] is `true`. By default:
/// * "On"
final String textOn;
/// Color shown when the [value] is `true`. By default:
/// * `Colors.green`
final Color colorOn;
/// Color shown when the [value] is `false`. By default:
/// * `Colors.red`
final Color colorOff;
/// The size of the text. By default:
/// * `14.0`
final double textSize;
/// The duration of the animation. By default:
/// * `Duration(milliseconds: 600)`
final Duration animationDuration;
/// Text displayed when the [value] is `true`. By default:
/// * `Icons.check`
final IconData iconOn;
/// Text displayed when the [value] is `false`. By default:
/// * `Icons.flag`
final IconData iconOff;
/// The width of the switch. By default:
/// * `130`
final double width;
/// Additional action on tap.
final Function? onTap;
/// Additional action on double tap.
final Function? onDoubleTap;
/// Additional action on swipe.
final Function? onSwipe;
const LiteRollingSwitch({
Key? key,
required this.value,
required this.onChanged,
this.textOff = "Off",
this.textOn = "On",
this.textSize = 14.0,
this.colorOn = Colors.green,
this.colorOff = Colors.red,
this.iconOff = Icons.flag,
this.iconOn = Icons.check,
this.animationDuration = const Duration(milliseconds: 600),
this.width = 130,
this.onTap,
this.onDoubleTap,
this.onSwipe,
}) : super(key: key);
#override
_RollingSwitchState createState() => _RollingSwitchState();
}
class _RollingSwitchState extends State<LiteRollingSwitch>
with SingleTickerProviderStateMixin {
late AnimationController animationController;
late Animation<double> animation;
late bool turnState;
double value = 0.0;
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
lowerBound: 0.0,
upperBound: 1.0,
duration: widget.animationDuration,
);
animation = CurvedAnimation(
parent: animationController,
curve: Curves.easeInOut,
);
animationController.addListener(() {
setState(() {
value = animation.value;
});
});
turnState = widget.value;
_determine();
}
#override
Widget build(BuildContext context) {
Color transitionColor = Color.lerp(widget.colorOff, widget.colorOn, value)!;
return GestureDetector(
onDoubleTap: () {
_action();
if (widget.onDoubleTap != null) widget.onDoubleTap!();
},
onTap: () {
_action();
if (widget.onTap != null) widget.onTap!();
},
onPanEnd: (details) {
_action();
if (widget.onSwipe != null) widget.onSwipe!();
},
child: Container(
padding: const EdgeInsets.all(5),
width: widget.width,
decoration: BoxDecoration(
color: transitionColor,
borderRadius: BorderRadius.circular(50),
),
child: Stack(
children: <Widget>[
// the "off" text
Transform.translate(
offset: Offset(10 * value, 0),
child: Opacity(
// its opacity will change
opacity: (1 - value).clamp(0.0, 1.0),
child: Container(
// it's in a container
padding: const EdgeInsets.only(right: 5),
alignment: Alignment.centerRight,
height: 40,
child: Text(
widget.textOff,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: widget.textSize,
),
),
),
),
),
// the "on" text
Transform.translate(
offset: Offset(10 * (1 - value), 0),
child: Opacity(
opacity: value.clamp(0.0, 1.0),
child: Container(
padding: const EdgeInsets.only(left: 5),
alignment: Alignment.centerLeft,
height: 40,
child: Text(
widget.textOn,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: widget.textSize,
),
),
),
),
),
// The icons
Transform.translate(
offset: Offset(widget.width / 2 * value, 0),
child: Transform.rotate(
angle: lerpDouble(0, 2 * pi, value)!,
// the white thing that will move
child: Container(
height: 40,
width: 40,
alignment: Alignment.center,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
children: <Widget>[
// The "on" icon
Center(
child: Opacity(
opacity: value.clamp(0.0, 1.0),
child: Icon(
widget.iconOn,
size: 21,
color: transitionColor,
),
),
),
// The "off" icon
Center(
child: Opacity(
opacity: (1 - value).clamp(0.0, 1.0),
child: Icon(
widget.iconOff,
size: 21,
color: transitionColor,
),
),
),
],
),
),
),
)
],
),
),
);
}
_action() {
_determine(changeState: true);
}
/// Handles the animation.
_determine({bool changeState = false}) {
setState(() {
if (changeState) turnState = !turnState;
(turnState)
? animationController.forward()
: animationController.reverse();
widget.onChanged(turnState);
});
}
}
This happens when I click on the "off" icon of the switch:
NOTE The switcher works, but you need to understand that my error doesn't always occur. In fact, it occurs just sometimes, when I try to turn off the switcher (and only when I click very precisely on the icon)
Please help me
Try move the _action function to only be called when onTap, onDoubleTap or onPanEnd isn't null.
onDoubleTap: () {
if (widget.onDoubleTap != null) {
widget.onDoubleTap!();
_action();
}
},
onTap: () {
if (widget.onTap != null) {
_action();
widget.onTap!();
}
},
onPanEnd: (details) {
if (widget.onSwipe != null) {
widget.onSwipe!();
_action();
}
},
Full code example:
import 'package:flutter/material.dart';
import 'dart:ui';
import 'dart:math';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({
Key? key,
required this.title,
}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: LiteRollingSwitch(
onChanged: (value) {},
onTap: () {},
value: false,
),
),
);
}
}
/// Customable and attractive Switch button.
/// Currently, you can't change the widget
/// width and height properties.
///
/// As well as the classical Switch Widget
/// from flutter material, the following
/// arguments are required:
///
/// * [value] determines whether this switch is on or off.
/// * [onChanged] is called when the user toggles the switch on or off.
class LiteRollingSwitch extends StatefulWidget {
/// A required boolean that sets the value of the button.
/// * "on" = `true`
/// * "off" = `false`
final bool value;
/// A function called every time there is a change in state. (required)
final Function(bool) onChanged;
/// Text displayed when the [value] is `false`. By default
/// * "Off"
final String textOff;
/// Text displayed when the [value] is `true`. By default:
/// * "On"
final String textOn;
/// Color shown when the [value] is `true`. By default:
/// * `Colors.green`
final Color colorOn;
/// Color shown when the [value] is `false`. By default:
/// * `Colors.red`
final Color colorOff;
/// The size of the text. By default:
/// * `14.0`
final double textSize;
/// The duration of the animation. By default:
/// * `Duration(milliseconds: 600)`
final Duration animationDuration;
/// Text displayed when the [value] is `true`. By default:
/// * `Icons.check`
final IconData iconOn;
/// Text displayed when the [value] is `false`. By default:
/// * `Icons.flag`
final IconData iconOff;
/// The width of the switch. By default:
/// * `130`
final double width;
/// Additional action on tap.
final Function? onTap;
/// Additional action on double tap.
final Function? onDoubleTap;
/// Additional action on swipe.
final Function? onSwipe;
const LiteRollingSwitch({
Key? key,
required this.value,
required this.onChanged,
this.textOff = "Off",
this.textOn = "On",
this.textSize = 14.0,
this.colorOn = Colors.green,
this.colorOff = Colors.red,
this.iconOff = Icons.flag,
this.iconOn = Icons.check,
this.animationDuration = const Duration(milliseconds: 600),
this.width = 140,
this.onTap,
this.onDoubleTap,
this.onSwipe,
}) : super(key: key);
#override
_RollingSwitchState createState() => _RollingSwitchState();
}
class _RollingSwitchState extends State<LiteRollingSwitch>
with SingleTickerProviderStateMixin {
late AnimationController animationController;
late Animation<double> animation;
late bool turnState;
double value = 0.0;
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
animationController = AnimationController(
vsync: this,
lowerBound: 0.0,
upperBound: 1.0,
duration: widget.animationDuration,
);
animation = CurvedAnimation(
parent: animationController,
curve: Curves.easeInOut,
);
animationController.addListener(() {
setState(() {
value = animation.value;
});
});
turnState = widget.value;
_determine();
}
#override
Widget build(BuildContext context) {
Color transitionColor = Color.lerp(widget.colorOff, widget.colorOn, value)!;
return GestureDetector(
onDoubleTap: () {
if (widget.onDoubleTap != null) {
widget.onDoubleTap!();
_action();
}
},
onTap: () {
if (widget.onTap != null) {
_action();
widget.onTap!();
}
},
onPanEnd: (details) {
if (widget.onSwipe != null) {
widget.onSwipe!();
_action();
}
},
child: Container(
padding: const EdgeInsets.all(5),
width: widget.width,
decoration: BoxDecoration(
color: transitionColor,
borderRadius: BorderRadius.circular(50),
),
child: Stack(
children: <Widget>[
// the "off" text
Transform.translate(
offset: Offset(10 * value, 0),
child: Opacity(
// its opacity will change
opacity: (1 - value).clamp(0.0, 1.0),
child: Container(
// it's in a container
padding: const EdgeInsets.only(right: 5),
alignment: Alignment.centerRight,
height: 40,
child: Text(
widget.textOff,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: widget.textSize,
),
),
),
),
),
// the "on" text
Transform.translate(
offset: Offset(10 * (1 - value), 0),
child: Opacity(
opacity: value.clamp(0.0, 1.0),
child: Container(
padding: const EdgeInsets.only(left: 5),
alignment: Alignment.centerLeft,
height: 40,
child: Text(
widget.textOn,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: widget.textSize,
),
),
),
),
),
// The icons
Transform.translate(
offset: Offset(widget.width / 1.6 * value, 0),
child: Transform.rotate(
angle: lerpDouble(0, 2 * pi, value)!,
// the white thing that will move
child: Container(
height: 40,
width: 40,
alignment: Alignment.center,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
),
child: Stack(
children: <Widget>[
// The "on" icon
Center(
child: Opacity(
opacity: value.clamp(0.0, 1.0),
child: Icon(
widget.iconOn,
size: 21,
color: transitionColor,
),
),
),
// The "off" icon
Center(
child: Opacity(
opacity: (1 - value).clamp(0.0, 1.0),
child: Icon(
widget.iconOff,
size: 21,
color: transitionColor,
),
),
),
],
),
),
),
)
],
),
),
);
}
_action() {
_determine(changeState: true);
}
/// Handles the animation.
_determine({bool changeState = false}) {
debugPrint(changeState.toString());
setState(() {
if (changeState) turnState = !turnState;
(turnState)
? animationController.forward()
: animationController.reverse();
widget.onChanged(turnState);
});
}
}