Add date time of build/run to title text in Flutter app - flutter

My current code in lib/main.dart looks like this:
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';
void main() => runApp(const App());
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: LoginForm(),
);
}
}
class CustomFieldBlocValidators implements FieldBlocValidators {
CustomFieldBlocValidators._();
static String? number(dynamic value) {
if (kDebugMode) {
print(value);
print(value is num);
}
if (value == null || value == false || num.tryParse(value) == null) {
return "Number input is required";
}
return null;
}
}
class LoginFormBloc extends FormBloc<String, String> {
final num1 = TextFieldBloc(
validators: [
FieldBlocValidators.required,
CustomFieldBlocValidators.number
],
);
final num2 = TextFieldBloc(
validators: [
FieldBlocValidators.required,
CustomFieldBlocValidators.number
],
);
LoginFormBloc() {
addFieldBlocs(
fieldBlocs: [
num1,
num2,
],
);
}
#override
void onSubmitting() async {
if (kDebugMode) {
print(DateTime.now());
print(num1.value);
print(num2.value);
}
await Future<void>.delayed(const Duration(seconds: 1));
if (Random().nextDouble() > 0.5) {
emitSuccess();
} else {
emitFailure(failureResponse: 'This is an awesome error!');
}
}
}
class LoginForm extends StatelessWidget {
const LoginForm({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginFormBloc(),
child: Builder(
builder: (context) {
final loginFormBloc = context.read<LoginFormBloc>();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: const Text('Basic Calculator - ')),
body: FormBlocListener<LoginFormBloc, String, String>(
onSubmitting: (context, state) {
LoadingDialog.show(context);
},
onSuccess: (context, state) {
LoadingDialog.hide(context);
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const SuccessScreen()));
},
onFailure: (context, state) {
LoadingDialog.hide(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.failureResponse!)));
},
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: AutofillGroup(
child: Column(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: loginFormBloc.num1,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Number 1',
prefixIcon: Icon(Icons.dialpad),
),
),
TextFieldBlocBuilder(
textFieldBloc: loginFormBloc.num2,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Number 2',
prefixIcon: Icon(Icons.dialpad),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
onPressed: loginFormBloc.submit,
child: const Text('ADD'),
),
ElevatedButton(
onPressed: loginFormBloc.submit,
child: const Text('SUBTRACT'),
),
],
)
],
),
),
),
),
);
},
),
);
}
}
class LoadingDialog extends StatelessWidget {
static void show(BuildContext context, {Key? key}) => showDialog<void>(
context: context,
useRootNavigator: false,
barrierDismissible: false,
builder: (_) => LoadingDialog(key: key),
).then((_) => FocusScope.of(context).requestFocus(FocusNode()));
static void hide(BuildContext context) => Navigator.pop(context);
const LoadingDialog({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: const EdgeInsets.all(12.0),
child: const CircularProgressIndicator(),
),
),
),
);
}
}
class SuccessScreen extends StatelessWidget {
const SuccessScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(Icons.tag_faces, size: 100),
const SizedBox(height: 10),
const Text(
'Success',
style: TextStyle(fontSize: 54, color: Colors.black),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
ElevatedButton.icon(
onPressed: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const LoginForm())),
icon: const Icon(Icons.replay),
label: const Text('AGAIN'),
),
],
),
),
);
}
}
In my class LoginForm where I set appBar: AppBar(title: const Text('Basic Calculator - ')), I want to show the date and time of build/run in front of the hypen. I am not able to achieve it any way.
I have tried:
const Text('Basic Calculator - '+DateTime.now())
assigned the DateTime.now() to a variable and used the variable
but to no avail.

Remove const and add toString()
Text('Basic Calculator - '+ DateTime.now().toString())
This works during run-time.

Related

Error: Could not find the correct Provider<RecipeProvider> above this AddRecipe Widget

`I want to access the provider in the AddRecipe page in order to save a new recipe and notify the listeners, in my case the list.builder in UserRecipes to rebuild.
Sorry for the big amount of code, I added everything that I thought may be useful. I just can't figure it out, have been trying for hours.
This is the error that I get:
ProviderNotFoundException (Error: Could not find the correct Provider<RecipeProvider> above this AddRecipe Widget
These are the files:
Wrapper.dart:
class Wrapper extends StatelessWidget {
const Wrapper({super.key});
#override
Widget build(BuildContext context) {
final user = Provider.of<User?>(context);
if (user == null) {
return const AuthPage();
} else {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => RecipeProvider(uid: user.uid))
],
child: const HomePage()
);
}
}
}
Home.dart:
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final AuthService _auth = AuthService();
// The 4 main application screens
static const List<Destination> allDestinations = <Destination>[
Destination(0, 'Home', Icons.home, Colors.teal),
Destination(1, 'Meals', Icons.dinner_dining, Colors.orange),
Destination(2, 'Recipes', Icons.restaurant_menu_sharp, Colors.amber),
Destination(3, 'Profile', Icons.person, Colors.blue),
];
int currentPageIndex = 0;
final screens = [
Center(child: Text('Home'),),
Center(child: Text('Meals'),),
RecipesPage(),
Center(child: Text('My profile'),),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: allDestinations[currentPageIndex].title, backButton: false, signOutButton: true),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) {
setState(() {
currentPageIndex = index;
});
},
selectedIndex: currentPageIndex,
destinations: allDestinations.map((Destination destination) {
return NavigationDestination(
icon: Icon(destination.icon, color: destination.color),
label: destination.title
);
}).toList(),
),
body: screens[currentPageIndex]
);
}
}
class Destination {
const Destination(this.index, this.title, this.icon, this.color);
final int index;
final String title;
final IconData icon;
final MaterialColor color;
}
Recipes.dart:
const List<Widget> recipes = <Widget>[
Text('My recipes'),
Text('Other recipes')
];
class RecipesPage extends StatefulWidget {
const RecipesPage({super.key});
#override
State<RecipesPage> createState() => _RecipesPageState();
}
class _RecipesPageState extends State<RecipesPage> {
final List<bool> _selectedRecipes = <bool>[true, false];
final _searchContoller = TextEditingController();
#override
void dispose() {
_searchContoller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var provider = context.watch<RecipeProvider>();
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 20),
ToggleButtons(
direction: Axis.horizontal,
onPressed: (int index) {
setState(() {
for (int i = 0; i < _selectedRecipes.length; i++) {
_selectedRecipes[i] = i == index;
}
});
},
borderRadius: const BorderRadius.all(Radius.circular(12)),
selectedBorderColor: Colors.blue[700],
selectedColor: Colors.white,
fillColor: Colors.blue[200],
color: Colors.blue[400],
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 165.0,
),
isSelected: _selectedRecipes,
children: recipes
),
SizedBox(height: 10),
// Search textfield
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: TextField(
controller: _searchContoller,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(12),
),
hintText: 'Search',
fillColor: Colors.grey[250],
filled: true
),
),
),
SizedBox(height: 20),
Expanded(
child: _getRecipePage(),
),
],
)
),
),
floatingActionButton: Consumer<RecipeProvider>(
builder: (context, value, child) {
return _getFAB();
},
)
);
}
Widget _getFAB() {
if (_selectedRecipes[1]) {
return Container();
} else {
return FloatingActionButton(
child: Icon(Icons.add, size: 35),
onPressed: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => AddRecipe()
)
),
},
);
}
}
Widget _getRecipePage() {
if (_selectedRecipes[0]) {
return UserRecipesWidget(search: _searchContoller.text.trim());
} else {
return OtherRecipesWidget();
}
}
}
User_recipes.dart:
class UserRecipesWidget extends StatefulWidget {
UserRecipesWidget({super.key, required this.search});
String search;
#override
State<UserRecipesWidget> createState() => _UserRecipesWidgetState();
}
class _UserRecipesWidgetState extends State<UserRecipesWidget> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
var provider = context.watch<RecipeProvider>();
return FutureBuilder(
future: provider.getUserRecipesFuture,
builder: (BuildContext ctx, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.connectionState == ConnectionState.done) {
if (asyncSnapshot.hasError) {
return const Center(child: Text('Could not retreive recipes!'));
}
return ListView.builder(
itemCount: provider.recipes.length,
itemBuilder: (BuildContext ctx, int index) {
return GestureDetector(
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => RecipePage(recipe: provider.recipes[index])
)
),
},
child: RecipeCard(recipe: provider.recipes[index]),
);
}
);
} else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}
RecipeProvider:
class RecipeProvider extends ChangeNotifier {
late RecipeService _recipeService;
List<RecipeModel> recipes = [];
late Future getUserRecipesFuture;
final String uid;
RecipeProvider({ required this.uid }) {
_recipeService = RecipeService(uid: uid);
getUserRecipesFuture = _getUserRecipesFuture();
}
Future _getUserRecipesFuture() async {
recipes = await _recipeService.getUserRecipes();
addDummyRecipe();
}
addDummyRecipe() {
recipes.add(RecipeModel(uid: "test", userId: "test", recipeName: "Pork"));
recipes.add(RecipeModel(uid: "test1", userId: "test1", recipeName: "Pizza"));
recipes.add(RecipeModel(uid: "test2", userId: "test2", recipeName: "Burger"));
notifyListeners();
}
}
And the main one that gives the error, add_recipe.dart:
class AddRecipe extends StatefulWidget {
const AddRecipe({super.key});
#override
State<AddRecipe> createState() => _AddRecipeState();
}
class _AddRecipeState extends State<AddRecipe> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: 'Add recipe', backButton: true),
body: SafeArea(
child: Center(
child: Column(
children: [
Text('Add recipe'),
SizedBox(height: 50),
// Add recipe button
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: GestureDetector(
onTap: () async {
context.read<RecipeProvider>().addDummyRecipe();
},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'Save recipe',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
),
),
]
)
)
),
);
}
}
`
the exception happens because you provided the RecipeProvider only for the HomePage(). means when you push a new route, it doesn't have that the RecipeProvider instance.
if you can't provide the RecipeProvider Golobally because it needs the uid, then you must provide it each time you push a new route
change your RecipePage Navigator to this
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: context.read<RecipeProvider>(),
child: RecipePage(recipe: provider.recipes[index]),
),
),
),
and your AddRecipe
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: context.read<RecipeProvider>(),
child: AddRecipe(),
),
),
),

When i refresh the page i couldn't get firestore realtime data with Flutter

I'm developing flutter web project. When I log in successfully at the first launch of the application, I can get the data in the firestore. However, when I refresh the web page or add new data after the data is received, the updated data does not appear on the screen, it is waiting in an if condition. Is this because Flutter can't draw the refreshed screen? What way can I follow?
**import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:moyes_home_project/view/candidate_addform.dart';
import 'package:moyes_home_project/view/constants.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import 'candidate_updateform.dart';
enum Menu { itemOne, itemTwo, itemThree }
class CandidatePool extends StatefulWidget {
const CandidatePool({Key? key}) : super(key: key);
#override
State<CandidatePool> createState() => _CandidatePoolState();
}
final Stream<QuerySnapshot> _usersStream =
FirebaseFirestore.instance.collection('candidateinfo').snapshots();
class _CandidatePoolState extends State<CandidatePool> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(defaultPadding),
child: Column(
// ignore: prefer_const_literals_to_create_immutables
children: [
const MyFiles(),
const Candidatepooltittle(),
const SizedBox(height: defaultPadding),
],
)));
}
}
class Candidatepooltittle extends StatefulWidget {
const Candidatepooltittle({Key? key}) : super(key: key);
#override
State<Candidatepooltittle> createState() => _CandidatepooltittleState();
}
class _CandidatepooltittleState extends State<Candidatepooltittle> {
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(defaultPadding),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return const CircularProgressIndicator();
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
}
return SizedBox(
width: double.infinity,
child: DataTable(
columnSpacing: defaultPadding,
columns: const [
DataColumn(
label: Text('test1'),
),
DataColumn(
label: Text('test2'),
),
DataColumn(
label: Text('test3'),
),
DataColumn(
label: Text('test4'),
),
DataColumn(
label: Text('test5'),
),
DataColumn(label: Text(''))
],
rows: snapshot.data!.docs
.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return DataRow(cells: [
DataCell(
Text(data['test5']),
),
DataCell(Text(data['test4']!)),
DataCell(Text(data['test3']!)),
DataCell(Text(data['test2']!)),
const DataCell(Text('test1')),
DataCell(Row(children: [
PopupMenuButton<Menu>(
padding: const EdgeInsets.only(
right: 2, left: 1, top: 2),
onSelected: (value) async {
if (value == Menu.itemOne) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => View(
url: data['fileUrl'],
)));
} else if (value == Menu.itemTwo) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
Candidateupdateform(document,
candidate: document)));
} else if (value == Menu.itemThree) {
final docdelete = FirebaseFirestore
.instance
.collection('candidateinfo')
.doc(document.id);
docdelete.delete();
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<Menu>>[
const PopupMenuItem<Menu>(
value: Menu.itemOne,
child: Text('test'),
),
const PopupMenuItem<Menu>(
value: Menu.itemTwo,
child: Text('test'),
),
const PopupMenuItem<Menu>(
value: Menu.itemThree,
child: Text('test'),
)
])
]))
]);
}).toList()));
})
]));
}
}
class View extends StatelessWidget {
PdfViewerController? _pdfViewerController;
final url;
View({Key? key, this.url}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('test'),
),
body: SfPdfViewer.network(
url,
controller: _pdfViewerController,
),
);
}
}
class MyFiles extends StatelessWidget {
const MyFiles({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final Size _size = MediaQuery.of(context).size;
return Column(children: [
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
ElevatedButton.icon(
style: TextButton.styleFrom(
// ignore: prefer_const_constructors
padding: EdgeInsets.symmetric(
horizontal: defaultPadding * 1.5,
),
),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => Candidateaddform()));
},
icon: const Icon(Icons.add),
label: const Text("test"),
),
])
]);
}
}**

Additional filters in Flutter

I am writing an application on Flutter. I was able to make one filter with multiple selections.
But I want to have additional filters.
I used Multi-Select, but in the case of multiple filters, I don't know how to apply it
With what help can I implement this?
import 'package:flutter/material.dart';
class MainPage extends StatelessWidget {
#override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("f"),
backgroundColor: Colors.black),
drawer: MediaQuery.of(context).size.width < 500 ? Drawer(
child: HomePage(),
) : null,
body: SafeArea(
child:Center(
child: MediaQuery.of(context).size.width < 500 ? Content() :
Row(
children: [
Container(
width: 200.0,
child: HomePage()
),
Container(
width: MediaQuery.of(context).size.width-200.0,
child: Content()
)
]
)
)
)
);
}
List devices_list = ["First device", "Second device", "Third device", "Fourth device", "Fifth device", "Sixth device", "Seventh device", "Eighth device", "Ninth device"];
class Content extends StatelessWidget{
#override
Widget build(context) =>
Scaffold(
backgroundColor: Colors.white,
body: LayoutBuilder(
builder: (context, constraints){
return AnimatedContainer(
duration: Duration(milliseconds: 500),
color: Colors.white,
child: Center(
child: Container(
constraints: BoxConstraints(
maxWidth: 800,),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),),
child: ListView.builder(
itemCount: devices_list.length,
itemBuilder: (BuildContext context, int index) {
return SizedBox (
height: 60,
key: Key(devices_list[index]),
child: Card(
shape: const RoundedRectangleBorder(
side: BorderSide(color: Colors.black,width: 3),
borderRadius: BorderRadius.all(Radius.circular(15))),
child: TextButton(
onPressed: (){},
child: ListTile(title: Text(devices_list[index]))),
)
);
}
))));
}));
}
class MultiSelect extends StatefulWidget {
final List<String> items;
const MultiSelect({Key? key, required this.items}) : super(key: key);
#override
State<StatefulWidget> createState() => _MultiSelectState();
}
class _MultiSelectState extends State<MultiSelect> {
// this variable holds the selected items
final List<String> _selectedItems = [];
// This function is triggered when a checkbox is checked or unchecked
void _itemChange(String itemValue, bool isSelected) {
setState(() {
if (isSelected) {
_selectedItems.add(itemValue);
} else {
_selectedItems.remove(itemValue);
}
});
}
// this function is called when the Cancel button is pressed
void _cancel() {
Navigator.pop(context);
}
// this function is called when the Submit button is tapped
void _submit() {
Navigator.pop(context, _selectedItems);
}
#override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Select Manufactures'),
content: SingleChildScrollView(
child: ListBody(
children: widget.items
.map((item) => CheckboxListTile(
value: _selectedItems.contains(item),
title: Text(item),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (isChecked) => _itemChange(item, isChecked!),
))
.toList(),
),
),
actions: [
TextButton(
child: const Text('Cancel'),
onPressed: _cancel,
),
ElevatedButton(
child: const Text('Submit'),
onPressed: _submit,
),
],
);
}
}
// Implement a multi select on the Home screen
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<String> _selectedItemsManufactures = [];
void _showMultiSelectManufactures() async {
// a list of selectable items
// these items can be hard-coded or dynamically fetched from a database/API
final List<String> _items = [
'Apple',
'Samsung',
'Xiaomi',
'Nokia',
'Huawei',
'Alcatel'
];
final List<String>? results = await showDialog(
context: context,
builder: (BuildContext context) {
return MultiSelect(items: _items);
},
);
// Update UI
if (results != null) {setState(() {_selectedItemsManufactures = results;});}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// use this button to open the multi-select dialog
ElevatedButton(
child: const Text('Manufactures'),
onPressed: _showMultiSelectManufactures,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.black)
),
),
const Divider(
height: 5,
color: Colors.white,
),
// display selected items
Wrap(
children: _selectedItemsManufactures
.map((e) => Chip(
label: Text(e),
))
.toList(),
)
],
),
),
);
}
}
Addition. Addition. I would like changes in the Menu class to be reflected in the class MainPage.
class DevicesPage extends StatelessWidget {
#override
Widget build(context) => Scaffold(
appBar: AppBar(title: Text("IT"),
backgroundColor: Colors.black),
drawer: MediaQuery.of(context).size.width < 500 ? Drawer(
child: Menu(),
) : null,
body: SafeArea(
child:Center(
child: MediaQuery.of(context).size.width < 500 ? MainPage() :
Row(
children: [
Container(
width: 200.0,
child: Menu()
),
Container(
width: MediaQuery.of(context).size.width-200.0,
child: MainPage()
)
]
)
)
)
);
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
Map<String, List<String>?> filters = {};
List<Phone> filteredPhones = phoneList;
#override
Widget build(BuildContext context) {
return Scaffold(
body: filteredPhones.isEmpty
? const Center(child: Text('No product', style: TextStyle(fontSize: 16),))
: ListView.builder(
itemCount: filteredPhones.length,
itemBuilder: (_, index) {
final currentPhone = filteredPhones[index];
return ListTile(
title: Text(currentPhone.name),
subtitle: Text('${currentPhone.brand}-${currentPhone.color}'),
trailing: Text('${currentPhone.operation_system}'),
);
}
),
);
}
}
class Menu extends StatefulWidget {
const Menu({Key? key}) : super(key: key);
#override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
Map<String, List<String>?> filters = {};
List<Phone> filteredPhones = phoneList;
void _filter() {
setState(() {
filteredPhones = phoneList;
filters.forEach((key, value) {
if((value ?? []).isNotEmpty) {
filteredPhones = filteredPhones.where((phone) {
switch(key) {
case 'brand':
return value!.contains(phone.brand);
case 'color':
return value!.contains(phone.color);
case 'operation_system':
return value!.contains(phone.operation_system);
return true;
default:
return false;
}
}).toList();
}
});
filters.clear();
Navigator.of(context).pop();
});
}
void _handleCheckFilter(bool checked, String key, String value) {
final currentFilters = filters[key] ?? [];
if(checked) {
currentFilters.add(value);
} else {
currentFilters.remove(value);
}
filters[key] = currentFilters;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('phones List'),
actions: [
IconButton(
icon: const Icon(Icons.filter_alt),
onPressed: () {
showDialog<Filter>(context: context, builder: (_) {
return SimpleDialog(
title: const Text('Filters',textAlign: TextAlign.center,),
contentPadding: const EdgeInsets.all(16),
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Select a brand'),
...brands.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'brand', el),
),
).toList(),
const Text('Select a operation_system'),
...operation_system.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'operation_system', el),
)
).toList(),
const Text('Select a colors'),
...colors.map((el) =>
CustomCheckboxTile(
label: el,
onChange: (check) => _handleCheckFilter(check, 'color', el),
),
).toList(),
const SizedBox(height: 24,),
ElevatedButton(onPressed: _filter, child: const Text('APPLY')),
],
),
],
);
});
},
),
],
),
body: filteredPhones.isEmpty
? const Center(child: Text('No product', style: TextStyle(fontSize: 16),))
: ListView.builder(
itemCount: filteredPhones.length,
itemBuilder: (_, index) {
final currentPhone = filteredPhones[index];
return ListTile(
title: Text(currentPhone.name),
subtitle: Text('${currentPhone.brand}-${currentPhone.color}'),
trailing: Text('${currentPhone.operation_system}'),
);
}
),
);
}
}
enter image description here
Well... you have many options. One way is to store all filters selected in a Map, like this:
final allFilters = {
"filterName1": "someValue",
"filterName2": "someOtherValue",
"fitlerName3": ["value1", "value2", "value3"]
}
And create a function that can handle each key of this map:
allFilter.forEach((key, value) {
switch(key) {
case "filterName1":
// ...some code
case "fitlerName2":
// ...more code
case "filterName3":
// ...much more code
}
})
You can check this demo project that I created https://github.com/felipeemidio/ListWithMultipleFilters

How to refresh data after returning to a screen in flutter

I have the following cart screen which contains the address fields. When a user wants to update their address, they click on Update button, which takes them to the address screen where they can update the address. Although when the back button is clicked to return back to the cart screen,the print function shows the updated value but the UI still shows the old value.
class CartScreen extends StatefulWidget {
static const routeName = '/cart';
#override
_CartScreenState createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
Future _addressFuture;
String value = 'deliver';
var address;
void initState() {
_addressFuture = _obtainAddressFuture();
super.initState();
}
Future _obtainAddressFuture() async {
address = await Provider.of<Addresses>(context, listen: false).fetchMyAddress();
}
void didChangeDependencies() async {
print('inside didchange');
super.didChangeDependencies();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
print('Rebuild cart');
return GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: FutureBuilder(
future: _addressFuture,
builder: (ctx, dataSnapshot) {
if (dataSnapshot.error != null) {
print('Data snapshot error is ' +
dataSnapshot.error.toString());
return Center(
child: Text('Something went wrong!'),
);
} else if (dataSnapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: Loading(),
);
} else {
return Container(
child: SingleChildScrollView(
child: Column(
children: [
Card(
margin: EdgeInsets.symmetric(
horizontal: 15,
vertical: 4,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
new GestureDetector(
onTap: () async{
_awaitReturnValueFromSecondScreen(context);
},
child: Container(
padding: EdgeInsets.all(10),
child: const Text("Update",
style: TextStyle(
color: Colors.blue))),
)
],
),
TextFormField(
initialValue: address.name,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.person,
color: Colors.orangeAccent),
labelText: 'Contact name'),
),
],
)),
],
)));
}
})));
}
void _awaitReturnValueFromSecondScreen(BuildContext context) async {
// start the SecondScreen and wait for it to finish with a result
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddressScreen('cartScreen'),
));
setState(() {
address = result;
});
print(address.name);
}
}
please check the below example as it requires async and await to get the data from 2nd screen
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Location',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: Provider(
create: (context) => Addresses(),
child: CartScreen(),
),
);
}
}
class CartScreen extends StatefulWidget {
static const routeName = '/cart';
#override
_CartScreenState createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
Future? _addressFuture;
String value = 'deliver';
var address;
void initState() {
_addressFuture = _obtainAddressFuture();
super.initState();
}
Future _obtainAddressFuture() async {
address =
await Provider.of<Addresses>(context, listen: false).fetchMyAddress();
}
void didChangeDependencies() async {
super.didChangeDependencies();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Your Cart'),
),
body: FutureBuilder(
future: _addressFuture,
builder: (ctx, dataSnapshot) {
if (dataSnapshot.error != null) {
print('Data snapshot error is ' + dataSnapshot.error.toString());
return const Center(
child: Text('Something went wrong!'),
);
} else if (dataSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
return SingleChildScrollView(
child: Column(
children: [
Card(
margin: const EdgeInsets.symmetric(
horizontal: 15,
vertical: 4,
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () async {
// Here you have to wait for the screen to retrun data so we use asyn await
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
AddressScreen(title: 'cartScreen'),
),
);
print(result);
},
child: Container(
padding: EdgeInsets.all(10),
child: const Text(
"Update",
style: TextStyle(
color: Colors.blue,
),
),
),
)
],
),
TextFormField(
// initialValue: address.name,
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(Icons.person,
color: Colors.orangeAccent),
labelText: 'Contact name',
),
),
],
),
),
],
),
);
}
},
),
),
);
}
}
class AddressScreen extends StatelessWidget {
const AddressScreen({
Key? key,
required this.title,
}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: TextButton(
onPressed: () {
// Here you have pass the data that you want
Navigator.pop(context, 'Data pass here ');
},
child: Text('Pass data back'),
),
),
);
}
}
class Addresses {
Future fetchMyAddress() async {}
}

When pushing new route in Flutter using flutter_bloc library doesn't work the context as I expected

When I login using the form in the signin_content.dart, I navigate to the home page. And then I can logout because I didn't push to another screen, so it works perfectly.
The problem is when I start the app, I see the first page that is signin_content.dart.
And then, if I push to signup_content.dart and I back to signin_content.dart and try to login, the context in the provider doesn't work.
BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn()),
I already tried this solution but I need to see how to locate the code:
link
I tried to use the the provider as a parent of MaterialApp but how?
I am using this library:
Login Flow
main.dart
// imports ..
class SimpleBlocDelegate extends BlocDelegate {
#override
void onEvent(Bloc bloc, Object event) {
super.onEvent(bloc, event);
print(event);
}
#override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print(transition);
}
#override
void onError(Bloc bloc, Object error, StackTrace stacktrace) {
super.onError(bloc, error, stacktrace);
print(error);
}
}
void main() {
BlocSupervisor.delegate = SimpleBlocDelegate();
final userRepository = UserRepository();
runApp(
BlocProvider<AuthenticationBloc>(
create: (context) {
return AuthenticationBloc(userRepository: userRepository)
..add(AppStarted());
},
child: App(userRepository: userRepository),
),
);
}
app.dart
// imports..
class App extends StatelessWidget {
final UserRepository userRepository;
const App({
Key key,
#required this.userRepository,
}) : assert(userRepository != null),
super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: fitnessTheme(),
routes: Routes.appRoutes,
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
if (state is AuthenticationLoading) {
return LoadingIndicator();
}
if (state is AuthenticationUnauthenticated) {
return SigninScreen();
}
if (state is AuthenticationProfileInactive) {
return WelcomeScreen();
}
if (state is AuthenticationAuthenticated) {
return HomeScreen();
}
return Splash();
},
),
);
}
}
signin_content.dart
//imports..
class SigninContent extends StatefulWidget {
SigninContent({Key key}) : super(key: key);
_SigninContentState createState() => _SigninContentState();
}
class _SigninContentState extends State<SigninContent> {
SigninFormBloc _signinFormBloc;
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final UserProvider _userProvider = UserProvider();
#override
void initState() {
super.initState();
_signinFormBloc = BlocProvider.of<SigninFormBloc>(context);
_emailController.addListener(_onEmailChanged);
_passwordController.addListener(_onPasswordChanged);
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
print(BlocProvider.of<AuthenticationBloc>(context).state);
return BlocBuilder<SigninFormBloc, SigninFormState>(
builder: (context, state) {
if (state.formSubmittedSuccessfully) {
final authData = {
'email': _emailController.value.text,
'password': _passwordController.value.text,
};
_userProvider.signin(
data: authData,
success: () =>
BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn()),
error: (message) {
Scaffold.of(context).showSnackBar(
buildSnackbar(
message,
Colors.red[700],
Colors.white,
Duration(seconds: 2),
),
);
},
);
// _emailController.clear();
// _passwordController.clear();
_signinFormBloc.add(FormReset());
}
return Column(
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(
top: 29.0,
left: 14.0,
right: 14.0,
),
child: Column(
children: <Widget>[
textInputWidget(
controller: _emailController,
labelText: "Email",
hintText: 'Enter a valid email',
autovalidate: state.email.isEmpty ? false : true,
validator: (_) {
return state.isEmailValid ? null : 'Invalid Email';
},
),
SizedBox(height: 20.0),
textInputWidget(
controller: _passwordController,
labelText: "Password",
hintText: 'Enter a valid password',
obscureText: true,
autovalidate: state.password.isEmpty ? false : true,
validator: (_) {
return state.isPasswordValid
? null
: 'Invalid Password';
},
),
SizedBox(height: 20.0),
primaryButton(
caption: "sign in",
context: context,
submit: state.isFormValid ? _onSubmitPressed : null,
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Text("Need an account?"),
FlatButton(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
textColor: Color(0xFF32AEE2),
child: Text(
"Sign up",
style: TextStyle(
fontSize: 14.0,
fontFamily: "SF Pro Text",
),
),
onPressed: () => Navigator.pushReplacementNamed(
context,
SignupScreen.routeName,
),
)
],
),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("Forgot your password?"),
FlatButton(
padding: EdgeInsets.all(0),
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
textColor: Color(0xFF32AEE2),
child: Text(
"Reset",
style: TextStyle(
fontSize: 14.0,
fontFamily: "SF Pro Text",
),
),
onPressed: () => Navigator.pushReplacementNamed(
context,
PasswordResetScreen.routeName,
),
)
],
),
),
],
),
),
),
),
],
);
},
);
}
#override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
void _onEmailChanged() {
_signinFormBloc.add(EmailChanged(email: _emailController.text));
}
void _onPasswordChanged() {
_signinFormBloc.add(PasswordChanged(password: _passwordController.text));
}
void _onSubmitPressed() {
_signinFormBloc.add(FormSubmitted());
}
}
signup_content.dart
// imports..
class SignupContent extends StatefulWidget {
SignupContent({Key key}) : super(key: key);
_SignupContentState createState() => _SignupContentState();
}
class _SignupContentState extends State<SignupContent> {
SignupFormBloc _signupFormBloc;
final TextEditingController _nameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _dobController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final TextEditingController _passwordConfirmationController =
TextEditingController();
final UserProvider _userProvider = UserProvider();
#override
void initState() {
super.initState();
_signupFormBloc = BlocProvider.of<SignupFormBloc>(context);
_nameController.addListener(_onNameChanged);
_emailController.addListener(_onEmailChanged);
_dobController.addListener(_onDobChanged);
_passwordController.addListener(_onPasswordChanged);
_passwordConfirmationController.addListener(_onPasswordConfirmationChanged);
}
#override
Widget build(BuildContext context) {
final Size _size = MediaQuery.of(context).size;
print(BlocProvider.of<AuthenticationBloc>(context).state);
return BlocBuilder<SignupFormBloc, SignupFormState>(
builder: (context, state) {
if (state.formSubmittedSuccessfully) {
final userData = {
'name': _nameController.value.text,
'email': _emailController.value.text,
'password': _passwordController.value.text,
};
_userProvider.signup(
userData: userData,
success: () {
BlocProvider.of<AuthenticationBloc>(context).add(
SignedUp(
email: userData["email"],
password: userData["password"],
),
);
},
error: (message) {
Scaffold.of(context).showSnackBar(
buildSnackbar(
message,
Colors.red[700],
Colors.white,
Duration(seconds: 2),
),
);
},
);
_signupFormBloc.add(FormReset());
}
return Column(
children: <Widget>[
authHeader(_size),
Expanded(
child: SingleChildScrollView(
child: Container(
margin: EdgeInsets.only(top: 16.0),
padding: EdgeInsets.only(
top: 29.0,
left: 14.0,
right: 14.0,
),
child: Column(
children: <Widget>[
textInputWidget(
controller: _nameController,
labelText: "Name",
hintText: 'Enter a valid name',
autovalidate: state.name.isEmpty ? false : true,
validator: (_) {
return state.isNameValid
? null
: 'At least 6 characters long.';
},
),
SizedBox(height: 20.0),
textInputWidget(
controller: _emailController,
labelText: "Email",
hintText: 'Enter a valid email',
autovalidate: state.email.isEmpty ? false : true,
validator: (_) {
return state.isEmailValid ? null : 'Invalid Email';
},
),
SizedBox(height: 20.0),
textInputWidget(
controller: _passwordController,
labelText: "Password",
hintText: 'Enter a valid password',
obscureText: true,
autovalidate: state.password.isEmpty ? false : true,
validator: (_) {
return state.isPasswordValid
? null
: 'Invalid Password';
},
),
SizedBox(height: 20.0),
textInputWidget(
controller: _passwordConfirmationController,
labelText: "Password Confirmation",
hintText: 'Enter a valid password',
autovalidate:
state.passwordConfirmation.isEmpty ? false : true,
validator: (_) {
return state.isPasswordConfirmationValid
? null
: 'Invalid Password';
},
obscureText: true,
),
SizedBox(height: 20.0),
primaryButton(
caption: "sign up",
context: context,
submit: state.isFormValid ? _onSubmitPressed : null,
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Text("Already Registered?"),
FlatButton(
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
textColor: Color(0xFF32AEE2),
child: Text(
"Sign in",
style: TextStyle(
fontSize: 14.0,
fontFamily: "SF Pro Text",
),
),
onPressed: () => Navigator.pushReplacementNamed(
context,
SigninScreen.routeName,
),
)
],
),
],
),
),
),
),
],
);
},
);
}
#override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_passwordController.dispose();
_passwordConfirmationController.dispose();
super.dispose();
}
void _onNameChanged() {
_signupFormBloc.add(NameChanged(name: _nameController.text));
}
void _onEmailChanged() {
_signupFormBloc.add(EmailChanged(email: _emailController.text));
}
void _onPasswordChanged() {
_signupFormBloc.add(PasswordChanged(password: _passwordController.text));
}
void _onPasswordConfirmationChanged() {
_signupFormBloc.add(
PasswordConfirmationChanged(
password: _passwordController.text,
passwordConfirmation: _passwordConfirmationController.text,
),
);
}
void _onSubmitPressed() {
_signupFormBloc.add(
FormSubmitted(),
);
}
}
This is because in the SignupContent you are replacement the home of your MaterialApp, so the BlocBuilder is removed and the widget does not change even if the authentication state changes.
To fix it, I recommend that you delete the BlocBuilder and use a BlocListener in each screen instead, in this way you can use the Navigator without any problem.
So change your main to
void main() {
BlocSupervisor.delegate = SimpleBlocDelegate();
final userRepository = UserRepository();
final authenticationBloc = AuthenticationBloc(userRepository: userRepository);
authenticationBloc
.firstWhere((state) =>
state is! AuthenticationUninitialized &&
state is! AuthenticationLoading)
.then((state) => runApp(App(
authenticationBloc: authenticationBloc,
home: state is AuthenticationUnauthenticated
? SigninScreen()
: HomeScreen(),
)));
authenticationBloc.add(AppStarted());
}
Also change your App widget
class App extends StatelessWidget {
const App({
Key key,
#required this.home,
#required this.authenticationBloc,
}) : super(key: key);
final Widget home;
final AuthenticationBloc authenticationBloc;
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: <BlocProvider>[
BlocProvider<AuthenticationBloc>.value(value: authenticationBloc),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: fitnessTheme(),
routes: Routes.appRoutes,
home: home,
),
);
}
}
Then use a BlocListener in your SigninScreen and SignupScreen for navigate, and show loading dialog when the authentication state changes.
class SigninScreen extends StatelessWidget {
static const String routeName = "signin";
const SigninScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
ScreenRatio.setScreenRatio(
MediaQuery.of(context).size.height,
MediaQuery.of(context).size.width,
);
return BlocProvider(
create: (context) => SigninFormBloc(),
child: BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state is AuthenticationLoading) {
LoadingDialog.show(context);
} else if (state is AuthenticationUnauthenticated) {
LoadingDialog.hide(context);
} else if (state is AuthenticationAuthenticated) {
LoadingDialog.hide(context);
Navigator.of(context).pushNamedAndRemoveUntil(
HomeScreen.routeName, (route) => false);
}
},
child: Scaffold(
body: SafeArea(
child: SigninContent(),
),
),
));
}
}
class SignupScreen extends StatelessWidget {
static const String routeName = "signup";
const SignupScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
ScreenRatio.setScreenRatio(
MediaQuery.of(context).size.height, MediaQuery.of(context).size.width);
return BlocProvider(
create: (context) => SigninFormBloc(),
child: BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state is AuthenticationLoading) {
LoadingDialog.show(context);
} else if (state is AuthenticationUnauthenticated) {
LoadingDialog.hide(context);
} else if (state is AuthenticationAuthenticated) {
LoadingDialog.hide(context);
Navigator.of(context).pushNamedAndRemoveUntil(
HomeScreen.routeName, (route) => false);
}
},
child: Scaffold(
body: SafeArea(
child: SignupContent(),
),
),
));
}
}
class LoadingDialog extends StatelessWidget {
static void show(BuildContext context, {Key key}) {
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => LoadingDialog(key: key),
);
}
static void hide(BuildContext context) {
Navigator.pop(context);
}
LoadingDialog({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: EdgeInsets.all(12.0),
child: CircularProgressIndicator(),
),
),
),
);
}
}
And finally in your HomeScreen use a BlocListener to navigate when the user has signed out
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state is AuthenticationUnauthenticated) {
Navigator.of(context).pushNamedAndRemoveUntil(
SigninScreen.routeName, (route) => false);
}
},
child: Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {
BlocProvider.of<AuthenticationBloc>(context).add(LoggedOut());
},
child: Text("Sign out"),
),
),
),
);