Related
I'm developing an app to make meal reservations, and I have an ElevatedButton that opens an alert when pressed. This alert is where the user is able to confirm the reservation. So, the alert has 2 text buttons, and I need that when the "sim" button is pressed, the ElevatedButton changes from "Reservar" with green color to "Cancelar Reserva" with red color.
I tried this way but it doesn't work:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../components/meal_item.dart';
import '../models/day_of_week.dart';
import '../models/want_to_comment.dart';
import '../models/meal.dart';
import '../utils/app_routes.dart';
import '../data/dummy_data.dart';
enum StatusReserva { y, n }
Color statusToColor(StatusReserva value) {
Color color = Colors.green;
switch (value) {
case StatusReserva.n:
break;
case StatusReserva.y:
color = Colors.red;
break;
}
return color;
}
String statusToString(StatusReserva value) {
String title = 'Reservar';
switch (value) {
case StatusReserva.n:
break;
case StatusReserva.y:
title = 'Cancelar Reserva';
break;
}
return title;
}
class DaysOfWeekMealsScreen extends StatefulWidget {
final List<Meal> meals;
final StatusReserva status;
final Function(StatusReserva) onStatusChanged;
const DaysOfWeekMealsScreen({
Key? key,
required this.meals,
required this.status,
required this.onStatusChanged,
}) : super(key: key);
#override
State<DaysOfWeekMealsScreen> createState() => _DaysOfWeekMealsScreenState();
}
class _DaysOfWeekMealsScreenState extends State<DaysOfWeekMealsScreen> {
StatusReserva status = StatusReserva();
#override
void initState() {
super.initState();
status = widget.status;
}
#override
Widget build(BuildContext context) {
final dayOfWeek = ModalRoute.of(context)!.settings.arguments as DayOfWeek;
final dayOfWeekMeals = widget.meals.where((meal) {
return meal.days_of_week.contains(dayOfWeek.id);
}).toList();
void _incrementCount(BuildContext context) {
dayOfWeek.count++;
}
Future<void> _showMyDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
' ',
),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text(
'Confirmar reserva para o dia XX/XX/XX?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Raleway',
fontWeight: FontWeight.bold),
),
],
),
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: const Text('Sim'),
onPressed: () => {
_incrementCount,
Navigator.pop(context),
status = StatusReserva.y
},
),
TextButton(
child: const Text('Não'),
onPressed: () => Navigator.pop(context),
),
],
),
],
);
},
);
}
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset(
'assets/images/logo.png',
fit: BoxFit.contain,
height: 32,
),
Container(
padding: const EdgeInsets.all(8.0),
child: Text(dayOfWeek.title)),
],
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
margin: const EdgeInsets.all(10),
child: ElevatedButton(
onPressed: () => {_showMyDialog(context)},
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
padding: MaterialStateProperty.all(const EdgeInsets.all(15)),
backgroundColor:
MaterialStateProperty.all(statusToColor(widget.status)),
),
child: Text(statusToString(widget.status)),
),
),
Expanded(
child: ListView.builder(
itemCount: dayOfWeekMeals.length,
itemBuilder: (ctx, index) {
return MealItem(dayOfWeekMeals[index]);
},
),
),
],
),
);
}
}
import 'package:apetit_project/models/want_to_comment.dart';
import 'package:apetit_project/screens/login_screen.dart';
import 'package:flutter/material.dart';
import 'screens/tabs_screen.dart';
import 'screens/days_of_week_meals_screen.dart';
import 'screens/meal_detail_screen.dart';
import 'screens/settings_screen.dart';
import 'screens/want_to_comment_screen.dart';
import 'screens/comment_screen.dart';
import 'utils/app_routes.dart';
import 'models/meal.dart';
import 'models/settings.dart';
import 'data/dummy_data.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Settings settings = Settings();
StatusReserva status = StatusReserva();
List<Meal> _gourmetMeals = [];
List<Meal> _lightMeals = [];
void _filterMeals(Settings settings) {
setState(() {
this.settings = settings;
});
}
void _reserveMeals(StatusReserva status) {
setState(() {
this.status = status;
});
}
void _toggleGourmet(Meal meal) {
setState(() {
_gourmetMeals.contains(meal)
? _gourmetMeals.remove(meal)
: _gourmetMeals.add(meal);
});
}
bool _isGourmet(Meal meal) {
return _gourmetMeals.contains(meal);
}
void _toggleLight(Meal meal) {
setState(() {
_lightMeals.contains(meal)
? _lightMeals.remove(meal)
: _lightMeals.add(meal);
});
}
bool _isLight(Meal meal) {
return _lightMeals.contains(meal);
}
#override
Widget build(BuildContext context) {
final ThemeData tema = ThemeData(
fontFamily: 'Raleway',
textTheme: ThemeData.light().textTheme.copyWith(
headline6: const TextStyle(
fontSize: 20,
fontFamily: 'Raleway',
fontWeight: FontWeight.bold,
),
),
);
return MaterialApp(
title: 'Appetit',
theme: tema.copyWith(
colorScheme: tema.colorScheme.copyWith(
primary: const Color.fromRGBO(222, 1, 59, 1),
secondary: const Color.fromRGBO(240, 222, 77, 1),
),
),
routes: {
AppRoutes.LOGIN: (ctx) => LoginScreen(),
AppRoutes.HOME: (ctx) => TabsScreen(_gourmetMeals, _lightMeals),
AppRoutes.WANT_TO_COMMENT: (ctx) => const WantToCommentScreen(),
AppRoutes.COMMENT: (ctx) => const CommentScreen(),
AppRoutes.DAYS_OF_WEEK_MEALS: (ctx) => DaysOfWeekMealsScreen(
meals: DUMMY_MEALS,
status,
_reserveMeals,
),
AppRoutes.MEAL_DETAIL: (ctx) => MealDetailScreen(
_toggleGourmet, _isGourmet, _toggleLight, _isLight),
AppRoutes.SETTINGS: (ctx) => SettingsScreen(settings, _filterMeals),
},
);
}
createMaterialColor(Color color) {}
}
To change the state of a widget you can use setState
enum StatusReserva { y, n }
Color statusToColor(StatusReserva value) {
Color color = Colors.green;
switch (value) {
case StatusReserva.n:
break;
case StatusReserva.y:
color = Colors.red;
break;
}
return color;
}
class StatusTest extends StatefulWidget {
const StatusTest({Key? key}) : super(key: key);
#override
State<StatusTest> createState() => _StatusTestState();
}
class _StatusTestState extends State<StatusTest> {
var status = StatusReserva.n;
Future<void> _showMyDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
' ',
),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text(
'Confirmar reserva para o dia XX/XX/XX?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Raleway',
fontWeight: FontWeight.bold),
),
],
),
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: const Text('Sim'),
onPressed: () => setState(() {
Navigator.pop(context);
status = StatusReserva.n;
}),
),
TextButton(
child: const Text('Não'),
onPressed: () => setState(() {
Navigator.pop(context);
status = StatusReserva.y;
}),
),
],
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 100,
height: 100,
child: Container(
color: statusToColor(status),
child: const Center(
child: Text(
'Status color',
style: TextStyle(color: Colors.white),
)),
),
),
ElevatedButton(
onPressed: () {
_showMyDialog(context);
},
child: const Text('Show dialog'),
),
],
),
),
);
}
}
For further clarification
The StatusReserva status = StatusReserva.n; should be in the State class _DaysOfWeekMealsScreenState. And to change it use setState. This value should never change if not inside a setState.
The Sim button onPressed should be like the following (Also with fixes of an unintended Set creation):
TextButton(
child: const Text('Sim'),
onPressed: () {
_incrementCount();
Navigator.pop(context);
setState(() => status = StatusReserva.y);
},
),
Use a boolean value instead of enum like this
bool isCancelReservar = false;
and in the state class before build method add this code :
bool isCancelReservar = false;
#override
void initState(){
isCancelReservar = widget.isCancelReservar;
super.initState();
}
Now you are able to update the value of isCancelReservar on clicking "sim button" using setState method. Your text button should look like this :
TextButton(
child: const Text('Sim'),
onPressed: () {
setState(() {
isCancelReservar = true;
});
_incrementCount,
Navigator.pop(context);
},
),
and your elevated button like this :
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
margin: const EdgeInsets.all(10),
child: ElevatedButton(
onPressed: () => {_showMyDialog(context)},
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
padding: MaterialStateProperty.all(const EdgeInsets.all(15)),
backgroundColor:
MaterialStateProperty.all(isCancelReservar ? Colors.red :
Colors.green),
),
child: Text(isCancelReservar ? "Cancelar Reserva" : "Reservar"),
),
),
where you can see the backgroundColor will be red with text "Cancelar Reserva" if the value of isCancelReservar is true else it would be of color green with test Reservar.
I am developing a task book, most of the tasks I have implemented but there is one that I can't solve, when I add a task it is added to the end of the list, and I want it to be at the top of the list. And also there is a problem when the theme changes, I want that if the theme is black, the text "Tasks +" were white and vice versa. Here is my code:
import 'package:flutter/material.dart';
void main(){
runApp(MaterialApp(
home: App(),
));
}
class ListItem{
String todoText;
bool todoCheck;
ListItem(this.todoText, this.todoCheck);
}
class _strikeThrough extends StatelessWidget{
final String todoText;
final bool todoCheck;
_strikeThrough(this.todoText, this.todoCheck) : super();
Widget _widget(){
if(todoCheck){
return Text(
todoText,
style: TextStyle(
fontSize: 22.0,
),
);
}
else{
return Text(
todoText,
style: TextStyle(
fontSize: 22.0
),
);
}
}
#override
Widget build(BuildContext context){
return _widget();
}
}
const Color ColorTextW = Colors.black;
class App extends StatefulWidget{
#override
AppState createState(){
return AppState();
}
}
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
late Color ColorType = Colors.black;
class AppState extends State<App> {
bool valText = true;
var counter = 0;
var IconsType = Icons.wb_sunny ;
late Color ColorType = Colors.black;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
List<ListItem> WidgetList = [];
#override
void dispose() {
textController.dispose();
popUpTextController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
appBar: AppBar(
title: Text("Список задач"),
actions: <Widget>[
IconButton(
icon: Icon(IconsType,color : ColorType
),
onPressed:() =>
{
if (_notifier.value == ThemeMode.light) {
_notifier.value = ThemeMode.dark,
IconsType = Icons.dark_mode,
ColorType = Colors.white,
} else
{
_notifier.value = ThemeMode.light,
IconsType = Icons.wb_sunny,
ColorType = Colors.black,
}
}
)
],
//backgroundColor: Colors.orange[500],
iconTheme: IconThemeData(
color: Colors.white
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: ColorTextW,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: () {
if (textController.text.replaceAll(" ", "").isNotEmpty) {
WidgetList.add(
new ListItem(textController.text.replaceAll(" ", ""), false));
setState(() {
valText = true;
textController.clear();
});
}
else
{
setState(() {
valText = false;
});
}
},
)
],
),
),
Container(
width: MediaQuery
.of(context)
.size
.height * 0.45,
child: TextField(
style: TextStyle(
fontSize: 22.0,
//color: Theme.of(context).accentColor,
),
controller: textController,
cursorWidth: 5.0,
autocorrect: true,
autofocus: true,
//onSubmitted: ,
),
),
Align(
child:
(valText == false) ?
Align(child: Text(("Задача пустая"),
style: TextStyle(
fontSize: 25.0, color: Colors.red)),
alignment: Alignment.center) :
Align(child: Text((""),),
alignment: Alignment.center)),
Expanded(
child: ReorderableListView(
children: <Widget>[
for(final widget in WidgetList)
GestureDetector(
key: Key(widget.todoText),
child: Dismissible(
key: Key(widget.todoText),
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
//key: ValueKey("Checkboxtile $widget"),
value: widget.todoCheck,
title: _strikeThrough(
widget.todoText, widget.todoCheck),
onChanged: (checkValue) {
//_strikethrough toggle
setState(() {
if (!checkValue!) {
widget.todoCheck = false;
}
else {
widget.todoCheck = true;
}
});
},
),
background: Container(
child: Icon(Icons.delete),
alignment: Alignment.centerRight,
color: Colors.redAccent,
),
direction: DismissDirection.endToStart,
movementDuration: const Duration(
milliseconds: 200),
onDismissed: (dismissDirection) { //Delete Todo
WidgetList.remove(widget);
},
),
)
],
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
var replaceWiget = WidgetList.removeAt(oldIndex);
WidgetList.insert(newIndex, replaceWiget);
});
},
),
)
],
),
)
);
}
);
}
}
In the onPressed method of your Add-IconButton do the following:
WidgetList.insert(0, new ListItem(textController.text.replaceAll(" ", ""), false));
This will insert the new item at the top of the existing list ^^
I am using BLE esp32 to get data from sensors but when I want to calibrate by getting data from the BLE device it gives me this error on Stream Builder I have used in the Device Setting up screen. A soon as I press Configuration it moves to the device set up page. I want is that, until I get value 1 from BLE it should keep on showing this screen
and once 1 comes it should go to the other screen to show a message and picture done like this and then automatically moves to Homepage after 2 seconds.
But it shows the bottom overflowed with the error msg "Bluetooth is not active", how can I avoid that? and jump for the next page? BLE is active as well since I checked the values were coming on another screen, but it is not happening on this screen for the connected device. Also the device done page it is not even going to Homescreen
I am new to FLutter that's why i have no idea what i should do with it.
Code for Device Set up Screen:
import 'dart:typed_data';
import 'package:epicare/ConfDeviceDone.dart';
import 'package:epicare/ConfigurationScreen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'Homepage.dart';
class ConfDeviceSet extends StatefulWidget {
const ConfDeviceSet({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_ConfDeviceSetState createState() => _ConfDeviceSetState();
}
class _ConfDeviceSetState extends State<ConfDeviceSet> {
// BLE
final String SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
//final String SERVICE_UUID = "0365a300-8d69-4066-80c7-554298a6ec5e";
//final String CHARACTERISTIC_UUID = "cf01c075-cb75-4dea-819e-2a79dd466bcb";
final String CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
bool isReady;
Stream<List<int>> stream;
List<int> lastValue;
List<double> traceDust = List();
#override
void initState() {
isReady = false;
discoverServices();
}
discoverServices() async {
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
//print(stream);
lastValue = characteristic.lastValue;
//print(lastValue);
setState(() {
isReady = true;
});
}
});
}
});
}
_dataParser(List<int> data) {
var value = Uint8List.fromList(data);
print("stream.value: $value"); // stream.value: [33]
var hr = ByteData.sublistView(value, 0, 1);
print("Heart rate: ${hr.getUint8(0)}");
return hr.getUint8(0); // Heart rate: 33
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: const Color(0xffE5E0A1),
elevation: 0,
centerTitle: true,
title: Text(
"Configuration",
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontFamily: 'Montserrat',
fontWeight: FontWeight.normal,
),
),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ConfigurationScreen();
},
),
);
},
),
),
body: Column(
children: [
Container(
padding: EdgeInsets.only(top: 65),
alignment: Alignment.center,
child: Text(
'Device setting up...',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
fontSize: 17,
color: const Color(0xa8000000),
height: 1.3333333333333333,
),
textHeightBehavior:
TextHeightBehavior(applyHeightToFirstAscent: false),
textAlign: TextAlign.center,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 120),
child: const SpinKitFadingCircle(
color: const Color(0xffF1C40E),
size: 200,
),
),
StreamBuilder<List<int>>(
stream: stream,
initialData: lastValue,
builder: (BuildContext context, AsyncSnapshot<List<int>> snapshot) {
var currentValue = _dataParser(snapshot.data);
if (currentValue == 1) {
print("Test Conf 1");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ConfDeviceDone(
device: widget.device,
);
},
),
);
}
},
),
],
),
);
}
}
Code for Device Done screen
import 'package:epicare/Homepage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'ConfigurationScreen.dart';
class ConfDeviceDone extends StatefulWidget {
const ConfDeviceDone({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_ConfDeviceDoneState createState() => _ConfDeviceDoneState();
}
class _ConfDeviceDoneState extends State<ConfDeviceDone> {
#override
void initState() {
// isReady = false;
// discoverServices();
var d = Duration(seconds: 2);
// delayed 5 seconds to next page
Future.delayed(d, () {
// to next page and close this page
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return Homepage(device: widget.device);
},
),
(route) => false,
);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: const Color(0xffE5E0A1),
elevation: 0,
centerTitle: true,
title: Text(
"Configuration",
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontFamily: 'Montserrat',
fontWeight: FontWeight.normal,
),
),
),
body: Column(
children: [
Container(
padding: EdgeInsets.only(top: 65),
alignment: Alignment.center,
child: Text(
'Device Configured Successfully!',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
fontSize: 15,
color: const Color(0xa8000000),
height: 1.3333333333333333,
),
textHeightBehavior:
TextHeightBehavior(applyHeightToFirstAscent: false),
textAlign: TextAlign.center,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 120),
child: Image.asset('assets/images/green-tick.gif'),
),
],
),
);
}
}
I have a Flutter code. instead of showing nothing when the submit button is clicked, I want to show the circular loading indicator when the button is clicked so to keep the user busy but I'm having a challenge to convert a tutorial I have that does that to a work with my code.
Here is the tutorial:
...
children: <Widget>[
new Padding(
padding: const EdgeInsets.all(16.0),
child: new MaterialButton(
child: setUpButtonChild(),
onPressed: () {
setState(() {
if (_state == 0) {
animateButton();
}
});
},
elevation: 4.0,
minWidth: double.infinity,
height: 48.0,
color: Colors.lightGreen,
),
)
],
Widget setUpButtonChild() {
if (_state == 0) {
return new Text(
"Click Here",
style: const TextStyle(
color: Colors.white,
fontSize: 16.0,
),
);
} else if (_state == 1) {
return CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
);
} else {
return Icon(Icons.check, color: Colors.white);
}
}
void animateButton() {
setState(() {
_state = 1;
});
Timer(Duration(milliseconds: 1000), () {
setState(() {
_state = 2;
});
});
Timer(Duration(milliseconds: 3300), () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => AnchorsPage(),
),
);
});
}
Here's my code. All I want to do is to display the CircularProgressIndicator when the system is performing the HTTP request.
And here is my code where I want to use the CircularProgressIndicator:
Center(
child:
RaisedButton(
padding: EdgeInsets.fromLTRB(80, 10, 80, 10),
color: Colors.green,
child: setUpButtonChild(),
onPressed: () {
setState(()async {
_state = 1;
var toSubmit = {
"oid": EopOid,
"modifiedBy": user['UserName'].toString(),
"modifiedOn": DateTime.now().toString(),
"submitted": true,
"submittedOn": DateTime.now().toString(),
"submittedBy": user['UserName'].toString()
};
for (EopLine i in selectedEops) {
var item = {
"oid": i.oid.toString(),
"quantityCollected": i.quantityCollected,
"modifiedBy": user['UserName'].toString(),
"modifiedOn": DateTime.now().toString(),
};
await http
.put(
"http://api.ergagro.com:112/UpdateEopLine",
headers: {
'Content-Type': 'application/json'
},
body: jsonEncode(item))
.then((value) async {
if (selectedEops.indexOf(i) ==
selectedEops.length - 1) {
await http
.put(
"http://api.ergagro.com:112/SubmitEop",
headers: {
'Content-Type':
'application/json'
},
body: jsonEncode(toSubmit))
.then((value) {
print('${value.statusCode} submitted');
Navigator.pop(context);
});
}
});
}
_state = 2;
});
//Navigator.of(context).push(MaterialPageRoute(
//builder: (context) =>
//StartScanPage(widget.dc_result)));
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
),
),
If you're using a button with the icon() constructor (icon + text), you can swap the icon with the CircularProgressIndicator when the button state changes. It works because both the icon and the indicator are widgets:
return ElevatedButton.icon(
onPressed: _isLoading ? null : _onSubmit,
style: ElevatedButton.styleFrom(padding: const EdgeInsets.all(16.0)),
icon: _isLoading
? Container(
width: 24,
height: 24,
padding: const EdgeInsets.all(2.0),
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 3,
),
)
: const Icon(Icons.feedback),
label: const Text('SUBMIT'),
);
Live Demo
You can copy paste run full code below
You can directly use package https://pub.dev/packages/progress_indicator_button
or reference it's source code
You can pass AnimationController to http job and use controller.forward and reset
code snippet
void httpJob(AnimationController controller) async {
controller.forward();
print("delay start");
await Future.delayed(Duration(seconds: 3), () {});
print("delay stop");
controller.reset();
}
...
ProgressButton(
borderRadius: BorderRadius.all(Radius.circular(8)),
strokeWidth: 2,
child: Text(
"Sample",
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
onPressed: (AnimationController controller) async {
await httpJob(controller);
}
working demo
full code
import 'package:flutter/material.dart';
import 'package:progress_indicator_button/progress_button.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void httpJob(AnimationController controller) async {
controller.forward();
print("delay start");
await Future.delayed(Duration(seconds: 3), () {});
print("delay stop");
controller.reset();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 200,
height: 60,
child: ProgressButton(
borderRadius: BorderRadius.all(Radius.circular(8)),
strokeWidth: 2,
child: Text(
"Sample",
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
onPressed: (AnimationController controller) async {
await httpJob(controller);
},
),
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
You can also use ternary operator to output based on some _isLoading state variable and make use of CircularProgressIndicator(), of course this is a simple solution without using any third party libraries.
#override
Widget build(BuildContext context) {
return TextButton(
onPressed: () {},
child: Container(
padding: const EdgeInsets.all(10),
child: _isLoading
? SizedBox(
height: 25,
width: 25,
child: CircularProgressIndicator(),
)
: Text('ORDER NOW'),
),
);
}
I'm using PaginatedDataTable widget to show data from api. It works perfectly, so I have set rowsPerPage to 4 and if my data length is for example 7, I get in my first page 4 rows with data and in the second one I get tree others and one empty row. That's the problem, on the second page, I want get only rows that contains data and no empty row. Guess that everyone understands what I mean.
Thank you in advance.
my code =>
class DashboardScreen extends StatefulWidget {
DashboardScreen(
{this.token,
this.f_name,
this.phone,
this.l_name,
this.type_client,
this.email,
this.addresse,
this.num_client});
String token;
final String f_name;
final String l_name;
final String email;
final String addresse;
final String num_client;
final String phone;
final String type_client;
#override
_DashboardScreenState createState() => _DashboardScreenState();
}
class _DashboardScreenState extends State<DashboardScreen>
with SingleTickerProviderStateMixin {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
AnimationController _animationController;
Animation<double> _animateIcon;
bool isOpened = false;
String token;
String f_name;
String l_name;
String email;
String addresse;
String num_client;
String phone, type_client;
bool showSpinner = false;
List<Transaction> transactions = [];
int rowsPerPAge = 4;
Future<List<Transaction>> _getTransactionsData() async {
setState(() {
showSpinner = true;
});
GetToken getToken = GetToken(token: "${widget.token}");
var freshToken = await getToken.getTokenData();
try {
Map<String, String> newHeader = {'Authorization': 'Bearer $freshToken'};
GetUserData getUserData = GetUserData(
'${APIConstants.API_BASE_URL}/...', newHeader);
var userData = await getUserData.getData();
//print(userData);
var apiError = userData['error'];
var apiCode = userData['code'];
try {
if (apiError == false && apiCode == 200) {
final items = userData['data'].cast<Map<String, dynamic>>();
List<Transaction> listOfTransactions = items.map<Transaction> ((json) {
return Transaction.fromJson(json);
}).toList();
setState(() {
showSpinner = false;
});
//print("succes: $userData");
return listOfTransactions;
}
else if(apiCode == 401){
setState(() {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
backgroundColor: MyColor.hintColor,
elevation: 10,
content: new Text(SnackBarText.TIME_OUT, style: GoogleFonts.roboto(color: Colors.white, fontSize: 20)),
));
showSpinner = false;
return MaterialPageRoute(builder: (context) => LoginScreen());
});
}
else {
//print("erreur: $userData");
setState(() {
_scaffoldKey.currentState.showSnackBar(new SnackBar(
backgroundColor: MyColor.hintColor,
elevation: 10,
content: new Text(SnackBarText.SERVER_ERROR,
style: GoogleFonts.roboto(color: Colors.white, fontSize: 20)),
));
return showSpinner = false;
});
}
} catch (e) {
print(e);
}
} catch (e) {
print(e);
print('1');
}
}
#override
void initState() {
super.initState();
token = widget.token;
f_name = widget.f_name;
l_name = widget.l_name;
email = widget.email;
addresse = widget.addresse;
num_client = widget.num_client;
phone = widget.phone;
type_client = widget.type_client;
_getTransactionsData().then((transactionsFromServer) {
transactions = transactionsFromServer;
//print(transactions);
});
_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
_animateIcon =
Tween<double>(begin: 1.0, end: 2.0).animate(_animationController);
}
#override
Widget build(BuildContext context) {
//FactureProvider factureProvider = Provider.of<FactureProvider>(context);
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
var orientation = MediaQuery.of(context).orientation;
bool portrait = orientation == Orientation.portrait;
return WillPopScope(
child: Scaffold(
key: _scaffoldKey,
backgroundColor: MyColor.myBackgroundColor,
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle: false,
backgroundColor: Colors.white,
leading: new IconButton(
padding: EdgeInsets.all(0.0),
icon: new Icon(
Icons.apps,
color: MyColor.menuColor,
),
onPressed: () => _scaffoldKey.currentState.openDrawer()),
title: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
//SizedBox(width: 30,),
//ImageIcon(AssetImage('images/notification.png'), color: Colors.black,),
Text(
Texts.DASHBOARD,
style: GoogleFonts.roboto(
color: MyColor.hintColor,
fontWeight: FontWeight.bold,
fontSize: 20),
textAlign: TextAlign.left,
),
SizedBox(
width: portrait ? width/4.0 : width/1.5,
),
GestureDetector(
onTap: _goToProfilScreen,
child: ImageIcon(
AssetImage('images/noun_avatar_1.png'),
color: Colors.black,
)),
],
),
),
),
drawer: buildDrawer,
body: Loader(
color: Colors.white.withOpacity(0.3),
loadIng: showSpinner,
child: ListView(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
Row(
children: <Widget>[
Conditioned(
cases:[
Case( (transactions?.length == 0 || transactions?.length == null), builder: () => Padding(
padding: const EdgeInsets.only(left: 10.0, top: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Text(Texts.HISTO_TRANSAC, style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w700), textAlign: TextAlign.start,)
],
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Center(child: Text(Texts.NO_TRANSAC, style: GoogleFonts.roboto(color: MyColor.hintColor, fontSize: 15),),),
),
],
),
)),
],
defaultBuilder: () => Padding(
padding: const EdgeInsets.only(left: 1.0, right: 1.0),
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
width: MediaQuery.of(context).size.width/1.005,
child: PaginatedDataTable(
header: Text(Texts.HISTO_TRANSAC, style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w700), textAlign: TextAlign.start,),
rowsPerPage: transactions.length <= rowsPerPAge ? transactions.length : rowsPerPAge,
horizontalMargin: 3.7,
columnSpacing: 1.8,
headingRowHeight: 15,
dataRowHeight: 30,
columns: [
DataColumn(label: Text(Texts.dATE, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.mONTANT_PAYE, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.SERVICE_TEXT, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.mODE_PAIEMENT, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
DataColumn(label: Text(Texts.DETAILS, style: GoogleFonts.roboto(fontSize: 8, fontWeight: FontWeight.w900))),
],
source: DTS(transactions, context, abonnementsById)
),
),
),
),
),
],
),
),
)
],
),
),
bottomNavigationBar: GestureDetector(
onTap: () => showMaterialModalBottomSheet(
context: context,
useRootNavigator: true,
bounce: true,
//secondAnimation: AnimationController.unbounded(vsync: this, duration: Duration(seconds: 30)),
enableDrag: true,
backgroundColor: Colors.transparent,
builder: (context, scrollController) => buildWrap(context),
),
child: Container(
color: MyColor.bottonNavColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
Texts.NEW,
style: GoogleFonts.roboto(
color: MyColor.hintColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 50,
width: 50,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: FloatingActionButton(
clipBehavior: Clip.hardEdge,
autofocus: true,
mini: true,
backgroundColor: MyColor.hintColor,
onPressed: () => showMaterialModalBottomSheet(
context: context,
useRootNavigator: true,
bounce: true,
//secondAnimation: AnimationController.unbounded(vsync: this, duration: Duration(seconds: 30)),
enableDrag: true,
backgroundColor: Colors.transparent,
builder: (context, scrollController) =>
buildWrap(context),
),
child: AnimatedIcon(
icon: AnimatedIcons.event_add,
size: 30,
progress: _animateIcon,
),
),
),
)
],
),
),
),
),
onWillPop: _onWillPop,
);
}
}
You can add a checker on PaginatedDataTable's onPageChanged to check if the number of items to be displayed on the table is less than the default number of rows.
PaginatedDataTable(
rowsPerPage: _rowsPerPage,
source: RowSource(),
onPageChanged: (int? n) {
/// value of n is the number of rows displayed so far
setState(() {
if (n != null) {
debugPrint(
'onRowsPerPageChanged $_rowsPerPage ${RowSource()._rowCount - n}');
/// Update rowsPerPage if the remaining count is less than the default rowsPerPage
if (RowSource()._rowCount - n < _rowsPerPage)
_rowsPerPage = RowSource()._rowCount - n;
/// else, restore default rowsPerPage value
else _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
} else
_rowsPerPage = 0;
});
},
columns: <DataColumn>[],
)
Here's the complete sample.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
/// Set default number of rows to be displayed per page
var _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: PaginatedDataTable(
rowsPerPage: _rowsPerPage,
source: RowSource(),
onPageChanged: (int? n) {
/// value of n is the number of rows displayed so far
setState(() {
if (n != null) {
debugPrint(
'onRowsPerPageChanged $_rowsPerPage ${RowSource()._rowCount - n}');
/// Update rowsPerPage if the remaining count is less than the default rowsPerPage
if (RowSource()._rowCount - n < _rowsPerPage)
_rowsPerPage = RowSource()._rowCount - n;
/// else, restore default rowsPerPage value
else _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
} else
_rowsPerPage = 0;
});
},
columns: [
DataColumn(
label: Text(
'Foo',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
DataColumn(
label: Text(
'Bar',
style: TextStyle(fontStyle: FontStyle.italic),
),
),
],
),
),
);
}
}
class RowSource extends DataTableSource {
final _rowCount = 26;
#override
DataRow? getRow(int index) {
if (index < _rowCount) {
return DataRow(cells: <DataCell>[
DataCell(Text('Foo $index')),
DataCell(Text('Bar $index'))
]);
} else
return null;
}
#override
bool get isRowCountApproximate => true;
#override
int get rowCount => _rowCount;
#override
int get selectedRowCount => 0;
}