Flutter widget not updating when using provider - flutter

i'm trying to implement provider in my flutter app but getting a little stuck.
It's basically a list of tasks. You should be able to double tap an existing task and edit the text. Below the task text is a counter which shows how long the task text is but it never updates when I type. Any ideas why?
task.dart
class Task {
String taskText;
bool completed;
String id = UniqueKey().toString();
Task({
this.taskText = '',
this.completed = false,
});
void toggle() {
completed = !completed;
}
}
task_data.dart
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:task_management/model/task.dart';
class TaskData with ChangeNotifier {
List<Task> _tasks = [];
UnmodifiableListView<Task> get tasks => UnmodifiableListView(_tasks);
void addTask(Task task) {
int index = _tasks.indexWhere((element) => element.id == task.id);
if (index == -1) {
_tasks.add(task);
} else {
_tasks[index] = task;
}
notifyListeners();
}
void toggleTask(Task task) {
task.toggle();
notifyListeners();
}
void removeTask(Task task) {
_tasks.remove(task);
notifyListeners();
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'model/task_data.dart';
import 'screens/home.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => TaskData(),
child: MaterialApp(
title: 'Provider Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: Home(),
),
);
}
}
home.dart
import 'package:flutter/material.dart';
import 'package:task_management/screens/task_list.dart';
import 'task_form.dart';
class Home extends StatelessWidget {
// create the appbar
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GeeksforGeeks'),
),
body: Container(
padding: EdgeInsets.all(20),
child: TaskList(),
),
floatingActionButton: FloatingActionButton(
child: Icon(
Icons.add,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TaskForm(
taskIndex: -1,
)));
},
),
);
}
}
task_list.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:task_management/model/task_data.dart';
import 'task_form.dart';
class TaskList extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<TaskData>(builder: (context, data, child) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: data.tasks.length,
itemBuilder: (context, index) {
final task = data.tasks[index];
// gesture detection
return GestureDetector(
onLongPress: () => data.removeTask(task),
onDoubleTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TaskForm(
taskIndex: index,
)));
},
child: Container(
margin: EdgeInsets.only(bottom: 10),
padding: EdgeInsets.fromLTRB(12, 5, 8, 5),
width: double.infinity,
decoration: BoxDecoration(color: Colors.black12, borderRadius: BorderRadius.circular(8)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// text field
Text(
task.taskText,
style: TextStyle(decoration: task.completed ? TextDecoration.lineThrough : null, fontSize: 16, fontWeight: FontWeight.bold),
),
// switch case
Switch(
value: task.completed,
onChanged: (c) => data.toggleTask(task),
),
],
),
),
);
},
);
});
}
}
task_form.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:task_management/model/task.dart';
import 'package:task_management/model/task_data.dart';
class TaskForm extends StatelessWidget {
final int taskIndex;
TaskForm({required this.taskIndex});
#override
Widget build(BuildContext context) {
TaskData taskProvider = Provider.of<TaskData>(context);
Task task = taskIndex != -1 ? taskProvider.tasks[taskIndex] : Task();
return Scaffold(
appBar: AppBar(
title: Text('Task Form'),
),
body: Container(
padding: EdgeInsets.all(18),
child: Column(
children: [
TextFormField(
initialValue: task.taskText,
onChanged: (c) => task.taskText = c,
),
Text(task.taskText.length.toString()),
// add button
ElevatedButton(
child: Text(
'Submit',
),
// assign action
onPressed: () {
Provider.of<TaskData>(context, listen: false).addTask(task);
Navigator.pop(context);
},
)
],
),
),
);
}
}

This happened as the Task object not notifying the changes,
Try
TextFormField(
initialValue: task.taskText,
onChanged: (c) {
task.taskText = c,
Provider.of<TaskData>(context, listen: false).notifyListeners();
}
),

Related

In Flutter CupertinoTabBar + StreamBuilder code is running but widgets are not rebuilt

I have a 2 tab screen where I want to dynamically rebuild the content depending on user's interactions. On the first tab there are multiple states which I try to handle by setting a current step name using model and provider. For the second screen after pressing a button I get the print message in a console but the content of CupertinoTabView remains unchanged.
Minimum runnable code snippet:
main.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_app/screens/login.dart';
import 'package:my_app/screens/tab_bar.dart';
import 'package:my_app/models/model.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
return runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => Model(),
),
],
child: CupertinoApp(
theme: const CupertinoThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: CupertinoColors.white,
),
initialRoute: '/',
routes: {
'/': (context) => LoginScreen(),
'/tab_bar': (context) => TabBarScreen(),
//'/add_item': (context) => AddItemScreen(),
},
),
);
}
}
login.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_app/models/model.dart';
class LoginScreen extends StatelessWidget {
final model = Model();
#override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('app',),
),
child: Container(
alignment: FractionalOffset.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: Text('next'),
onPressed: (){
model.setStep('step1');
Navigator.pushNamed(context, '/tab_bar');
},
style: ElevatedButton.styleFrom(
primary: CupertinoColors.white,
side: BorderSide(color: Colors.blue, width: 2.0)
),
),
]
),
),
);
}
}
tab_bar.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_app/models/model.dart';
class TabBarScreen extends StatefulWidget {
#override
State createState() => TabBarScreenState();
}
class TabBarScreenState extends State<TabBarScreen> {
final model = Model();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
print(model.getStep);
return Consumer<Model>(
builder: (context, model, _) => CupertinoTabScaffold(
tabBar: CupertinoTabBar(
backgroundColor: CupertinoColors.white,
inactiveColor: Colors.blue,
activeColor: Colors.blueAccent,
border: const Border(
top: BorderSide(
color: CupertinoColors.activeBlue,
width: 2.0,
),
),
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.favorite_outline_outlined),
),
BottomNavigationBarItem(
icon: Icon(Icons.more_vert_sharp),
),
],
),
tabBuilder: (context, index) {
late final CupertinoTabView returnValue;
switch (index) {
case 0:
if (model.getStep == 'step1') {
print('step1');
returnValue = CupertinoTabView(builder: (context) {
return CupertinoPageScaffold(
child: SafeArea(
child: GestureDetector(
onTap: (){ },
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.only(bottom: 15.0),
child: FloatingActionButton(
onPressed: () { model.setStep('step2'); setState(() {
}); },
tooltip: 'step2',
child: Icon(Icons.add),
backgroundColor: CupertinoColors.white,
foregroundColor: CupertinoColors.black,
),
),
],
),
],
),
),
),
);
});
}
else if (model.getStep == 'step2') {
print('step2');
returnValue = CupertinoTabView(builder: (context) {
return CupertinoPageScaffold(
child: SafeArea(
child: Column(
children: <Widget> [
Text('step2'),
],
),
),
);
});
}
else {
returnValue = CupertinoTabView(builder: (context) {
return Container();
});
}
break;
case 1:
returnValue = CupertinoTabView(builder: (context) {
return CupertinoPageScaffold(
child: Text("Tab2"),
);
});
break;
}
return returnValue;
},
),
);
}
}
model.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class Model extends ChangeNotifier {
static String currentStep = 'login';
// login
// step1 - step1
// step2 - step2
String get getStep {
return currentStep;
}
void setStep(String step) {
currentStep = step;
notifyListeners();
}
}
I want the tab to rebuild on pressing a button from step1 to show the step2 content. In a console I can see the print of 'step2' when pressing, but the actual tab remains unchanged.
Appreciate your help
Since nobody have answered I figured it out on my own.
The resolution is to use Navigator like this:
onPressed: () {
Navigator.of(context).push(
CupertinoPageRoute<void>(
builder: (BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Page 2 of tab'),
),
child: Center(
child: CupertinoButton(
child: const Text('Back'),
onPressed: () { Navigator.of(context).pop(); },
),
),
);
},
),
);
},
and therefore you won't need 'else if' conditioning.

Another exception was thrown: Error: Could not find the correct Provider<AppData> above this History Widget flutter

am facing this problem for a while now and i've no flutter community in my country so am forced to post online if i get any help and i will be glad if i found one cuz am in the deployment face of this app and it is the only thing stopping from publishing and getting paid so please guys i need help
[![am getting this error for a week now][1]][1]
this is my home
import 'package:connection_status_bar/connection_status_bar.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:taxi/Driver/HomeScreen.dart';
import 'package:taxi/Driver/carInfo..dart';
import 'package:taxi/Driver/loginPage.dart';
import 'package:taxi/config.dart';
import 'package:taxi/riders/Screens/HomeScreen.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:provider/provider.dart';
import 'package:taxi/riders/Screens/PhoneAuthInfo.dart';
import 'package:taxi/riders/Screens/Profile.dart';
import 'package:taxi/riders/Screens/historyScreen.dart';
import 'package:taxi/riders/Screens/landingPage.dart';
import 'package:taxi/riders/Screens/loginPage.dart';
import 'package:taxi/riders/Screens/signUp.dart';
import 'package:taxi/riders/data/Data.dart';
import 'package:taxi/Driver/signUp.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
currentfirebaseUser = FirebaseAuth.instance.currentUser;
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => AppData(),)
],
child: EasyLocalization(
child: MyApp(),
path: "images/resources",
saveLocale: true,
supportedLocales: [
Locale('am', 'ET'),
Locale('en', 'CA'),
Locale('en', 'US')
],
fallbackLocale: Locale('en', 'US'),
),
)
);
}
DatabaseReference userRef =
FirebaseDatabase.instance.reference().child("users");
DatabaseReference driverRef =
FirebaseDatabase.instance.reference().child("drivers");
DatabaseReference newRequestsRef =
FirebaseDatabase.instance.reference().child("Ride Request");
DatabaseReference rideRequestRef = FirebaseDatabase.instance
.reference()
.child("drivers")
.child(currentfirebaseUser.uid)
.child("newRide");
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Goosh',
theme: ThemeData(
cursorColor: Colors.black,
fontFamily: "Font regular",
primaryColor: Colors.amber,
),
builder: (context, _) => Stack(children: [
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Goosh',
theme: ThemeData(
cursorColor: Color(0xff5f86ba),
fontFamily: "Font regular",
primaryColor: Color(0xff5f86ba),
),
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
initialRoute: FirebaseAuth.instance.currentUser == null
? PhoneAuth.idScreen
: HomeScreen.idScreen,
// initialRoute: Login.idScreen,
routes: {
PhoneAuth.idScreen: (context) => PhoneAuth(),
Login.idScreen: (context) => Login(),
SignUp.idScreen: (context) => SignUp(),
HomeScreen.idScreen: (context) => HomeScreen(),
DriverSignUp.idScreen: (context) => DriverSignUp(),
DriverLogin.idScreen: (context) => DriverLogin(),
CarInfo.idScreen: (context) => CarInfo(),
DriverMainScreen.idScreen: (context) => DriverMainScreen(),
HistoryScreen.idScreen: (route) => HistoryScreen(),
PhoneAuthInfo.idScreen: (context) => PhoneAuthInfo(),
ProfilePage.idScreen: (context) => ProfilePage()
},
),
Align(
alignment: Alignment.topCenter,
child: ConnectionStatusBar(
lookUpAddress: 'google.com',
height: 32,
animationDuration: Duration(milliseconds: 100),
title: Material(
color: Colors.redAccent,
child: Text(
'Please check your internet connection',
style: TextStyle(color: Colors.white, fontSize: 16),
),
)),
)
]),
);
}
}
and this is how i navigate to this page
ListTile(
leading: Icon(Icons.local_taxi_outlined),
title: Text("My Gooshs".tr().toString()),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => HistoryScreen()));
},
),
and this is my history screen code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:taxi/riders/data/data.dart';
import 'package:taxi/riders/widget/historyItem.dart';
class HistoryScreen extends StatefulWidget {
static const String idScreen = "HistoryScreen";
#override
_HistoryScreenState createState() => _HistoryScreenState();
}
class _HistoryScreenState extends State<HistoryScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("History"),
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: Icon(Icons.keyboard_arrow_left),
),
),
body: History());
}
}
class History extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView.separated(
padding: EdgeInsets.all(0),
itemBuilder: (context, index) {
return HistoryItem(
history: Provider.of<AppData>(context, listen: false)
.tripHistoryDataList[index],
);
},
separatorBuilder: (BuildContext context, int index) => Divider(
height: 2.0,
thickness: 2.0,
),
itemCount: Provider.of<AppData>(context, listen: false).tripHistoryDataList.length,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
);
}
}
and this is my single history item
import 'package:flutter/material.dart';
import 'package:taxi/riders/assistance/DriverAssistMethod.dart';
import 'package:taxi/riders/model/History.dart';
class HistoryItem extends StatelessWidget {
final History history;
HistoryItem({this.history});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
children: [
Container(
child: Row(
children: <Widget>[
Image.asset(
'images/pickicon.png',
height: 16.0,
width: 16.0,
),
SizedBox(width: 18.0),
Expanded(
child: Container(
child: Text(history.pickUp,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 18.0)))),
Text(
'${history.fares}',
style: TextStyle(fontFamily: 'Font bold', fontSize: 16.0),
)
],
),
),
SizedBox(
height: 8.0,
),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Image.asset(
'images/desticon.png',
height: 16.0,
width: 16.0,
),
Text(history.dropOff,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 18.0))
],
),
SizedBox(
height: 15.0,
),
Text(
DriverAssistantMethods.formatTripDate(history.createdAt),
style: TextStyle(color: Colors.grey),
)
],
),
);
}
}
and this is my provider data
import 'package:taxi/riders/model/History.dart';
import 'package:taxi/riders/model/adress.dart';
import 'package:flutter/material.dart';
class AppData extends ChangeNotifier{
Address pickupLocation, dropOffLocation;
int countTrips = 0;
List<String> tripHistoryKeys = [];
List<History> tripHistoryDataList = [];
void updatePickUpLocationAddress(Address pickUpAddress)
{
pickupLocation = pickUpAddress;
notifyListeners();
}
void updatedropOffLocationAddress(Address dropOffAddress)
{
dropOffLocation = dropOffAddress;
notifyListeners();
}
void updateTripsCounter(int tripCounter){
countTrips = tripCounter;
notifyListeners();
}
void updateTripKeys(List<String> newKeys){
tripHistoryKeys = newKeys;
notifyListeners();
}
void updateTripData(History eachhistory){
tripHistoryDataList.add(eachhistory);
notifyListeners();
}
}
and this is how i fetch data from the realtime database
static void retrieveHistory(context) {
//retrieve history and Trip History
newRequestsRef
.orderByChild("rider_name")
.once()
.then((DataSnapshot dataSnapshot) {
if (dataSnapshot.value != null) {
//update total number of trip to provider
Map<dynamic, dynamic> keys = dataSnapshot.value;
int tripCounter = keys.length;
Provider.of<AppData>(context, listen: false)
.updateTripsCounter(tripCounter);
//update trip keys of trip to provider
List<String> tripHistoryKeys = [];
keys.forEach((key, value) {
tripHistoryKeys.add(key);
});
Provider.of<AppData>(context, listen: false)
.updateTripKeys(tripHistoryKeys);
obtainTripRequestHistoryData(context);
}
});
}
static void obtainTripRequestHistoryData(context) {
var keys = Provider.of<AppData>(context, listen: false).tripHistoryKeys;
for (String key in keys) {
newRequestsRef.child(key).once().then((DataSnapshot dataSnapshot) {
if (dataSnapshot.value != null) {
newRequestsRef
.child(key)
.child("rider_name")
.once()
.then((DataSnapshot dsnap) {
String name = dsnap.value.toString();
if (name == userCurrentInfo.name) {
var history = History.fromSnapshot(dataSnapshot);
Provider.of<AppData>(context, listen: false).updateTripData(history);
}
});
}
});
}
}
[1]: https://i.stack.imgur.com/sRBAD.jpg
I think main() is the bug, should
void main() {
var colorProvider = ColorProvider();
var counterProvider = CounterProvider();
Provider.debugCheckInvalidValueType = null;
runApp(MultiProvider(
providers: [
ChangeNotifierProvider.value(value: colorProvider),
ChangeNotifierProvider.value(value: counterProvider),
],
child: MyApp(),
));
}
you can look my demo: https://github.com/pheromone/Flutter_learn_demo/tree/master/%E5%85%B6%E4%BB%96/state%E7%AE%A1%E7%90%86/provider_demo4.x

Flutter - Cubit and some clarification needed

I'm really new in flutter and in Cubit pattern.
As far as I know Cubit is something quite new and now it is the base of the BloC pattern.
I' have tried to understand how it works and I have understood some concept and something about the state management and I have tried to build a simple app.
The app is connected with a API that respond with a list of Shops and have a BottomTabBar.
This is my code:
main.dart
import 'package:myapp/cubit/maison_cubit.dart';
import 'package:myapp/pages/maison.dart';
import 'package:myapp/pages/home.dart';
import 'package:myapp/repository/maisons_repository.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'My Cubit app',
home: BlocProvider(
create: (context) => MaisonCubit(MaisonsRepository()),
child: HomePage(),
),
routes: {
MaisonPage.routeName: (ctx) => MaisonPage(),
},
);
}
}
My maison_repository.dart just call an external API and add the maisons to a list.
import 'dart:convert';
import 'package:myapp/const.dart';
import 'package:http/http.dart' as http;
import '../models/maison.dart';
class MaisonsRepository {
List<Maison> items = [];
Future<List<Maison>> getMaisons() async {
final response = await http.get('https://get-maison.example', headers: {
"Accept": "application/json",
"content-type": "application/json",
});
if (response.statusCode == 200) {
items.clear();
List<dynamic> list = json.decode(response.body);
list.forEach((element) {
items.add(Maison.fromJson(element));
});
return items;
} else {
throw Exception('Failed to load maisons');
}
}
Maison find(String id) {
return items.firstWhere((element) => element.id == id);
}
}
This is home.dart. In the MaisonLoaded I call BottomBar that need to build the bottom bar and display the correct page. In all the docs I have read I haven't found a good explaination about how to manage the data after I get it from the repository, so I have added a constructor in my BottomBar and I have passed the data. Is it correct?
import 'package:myapp/bottom_bar.dart';
import 'package:myapp/cubit/maison_cubit.dart';
import 'package:myapp/pages/maisons_listing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
final maisonCubit = context.bloc<MaisonCubit>();
maisonCubit.getMaisons();
return BlocConsumer<MaisonCubit, MaisonState>(
listener: (context, state) {
if (state is MaisonError) {
return Container(
child: Text('Missing connection'),
);
}
},
builder: (context, state) {
if (state is MaisonLoading) {
return Container(
child: Text('My loader here'),
);
}
if (state is MaisonLoaded) {
return Container(
child: BottomBar(state.maisons),
);
}
return Container();
},
);
}
}
This is the BottomBar widget ( MapPage is just a container with a text in it )
import 'package:myapp/models/maison.dart';
import 'package:myapp/pages/maisons_listing.dart';
import 'package:myapp/pages/map.dart';
import 'package:flutter/material.dart';
class BottomBar extends StatefulWidget {
final List<Maison> maisons;
BottomBar(this.maisons);
#override
_BottomBarState createState() => _BottomBarState();
}
class _BottomBarState extends State<BottomBar> {
int _currentIndex = 0;
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
List<Widget> _children = [
MaisonListingPage(widget.maisons),
MapPage(widget.maisons),
];
return Scaffold(
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Color(0xFFDDCDC8),
currentIndex: _currentIndex,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text('Maisons'),
),
BottomNavigationBarItem(
icon: Icon(
Icons.map,
),
title: Text('Map'),
),
],
),
);
}
}
And this is the page that should display the maison's list.
import 'package:myapp/models/maison.dart';
import 'package:myapp/pages/maison.dart';
import 'package:flutter/material.dart';
class MaisonListingPage extends StatefulWidget {
final List<Maison> maisons;
MaisonListingPage(this.maisons);
#override
_MaisonListingPageState createState() => _MaisonListingPageState();
}
class _MaisonListingPageState extends State<MaisonListingPage> {
List<Maison> _currentList = [];
List<Maison> _maisons = [];
#override
Widget build(BuildContext context) {
_maisons = widget.maisons;
return SingleChildScrollView(
child: Column(
children: <Widget>[
HeroWidget(),
Transform.translate(
offset: Offset(0, -30),
child: Container(
height: 60.0,
padding: EdgeInsets.symmetric(vertical: 5.0),
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey[350],
blurRadius: 20.0,
offset: Offset(0, 10.0),
),
],
),
child: ListTile(
leading: Icon(Icons.search),
title: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: AppLocalizations.of(context)
.translate('home_search_input_placeholder'),
),
),
trailing: IconButton(
icon: Icon(
Icons.clear,
),
onPressed: () {
},
),
),
),
),
MediaQuery.removePadding(
context: context,
removeTop: true,
child: maisonListView(),
),
],
),
);
}
ListView maisonListView() {
return ListView.builder(
shrinkWrap: true,
primary: false,
itemCount: _currentList.length,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () => Navigator.of(context).pushNamed(
MaisonPage.routeName,
arguments: _currentList[index].id,
),
child: Text(_currentList[index].name),
);
},
);
}
onChange() {
setState(() {});
}
}
If I run the code, I can see the list of the maison. The problem comes with the tap on a single maison, I'd like to open a new page and show all the content.
So I have added a method in the maison_repository, if you check it you can see a find method.
In the single maison page I have tried to init the repository and use the find method in this way:
import 'dart:async';
import 'package:myapp/models/maison.dart';
import 'package:myapp/repository/maisons_repository.dart';
import 'package:flutter/material.dart';
class MaisonPage extends StatefulWidget {
static const routeName = '/single-maison';
#override
_MaisonPageState createState() => _MaisonPageState();
}
class _MaisonPageState extends State<MaisonPage> {
MaisonsRepository _maisonsRepository;
#override
Widget build(BuildContext context) {
String maisonId = ModalRoute.of(context).settings.arguments as String;
Maison maison = _maisonsRepository.find(maisonId);
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: MediaQuery.of(context).size.width * 0.70,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(maison.name),
background: MaisonHeroWidget(
id: maison.id,
imageUrl: maison.imageUrl,
),
),
),
SliverList(
delegate: SliverChildListDelegate(
[
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Text(
maison.description,
),
],
),
),
],
),
),
],
),
);
}
}
When I try to visit this page I get:
The following NoSuchMethodError was thrown building MaisonPage(dirty, dependencies: [_ModalScopeStatus], state: _MaisonPageState#335d9):
The method 'find' was called on null.
Receiver: null
Tried calling: find("2389")
The relevant error-causing widget was:
MaisonPage file:///Users/christiangiupponi/Dev/FlutterApp/myapp/lib/main.dart:63:40
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 _MaisonPageState.build (package:myapp/pages/maison.dart:69:40)
#2 StatefulElement.build (package:flutter/src/widgets/framework.dart:4619:28)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4502:15)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4675:11)
...
════════════════════════════════════════════════════════════════════════════════════════════════════
What am I missing?
Is this a good approach to get the data?
As mentioned in the comments, _maisonsRepository is yet to be initialized. As for the empty items List, you can call getMaisons when the repository has been initialized.

Could not find the correct Provider . The provider you are trying to read is in a different route

I have following route in my app:
Main.dart ---> SplashScreen.dart ---> DetailsPage.dart
Main.dart
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => FontSizeHandler()),
],
child: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SplashScreen(),
);
}
}
From SplashScreen.dart I move to DetailsPage.dart using Navigator.pushAndRemoveUntil i.e
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) => DetailsPage()), (route) => false);
Now in Details page on App Bar there is icon and on press of which I want to change the font using FontSizeHandler
DetailsPage.dart
class DetailsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.arrow_upward),
onPressed: () {
context.read<FontSizeHandler>().increaseFont();
},
),
IconButton(
onPressed: () {
context.read<FontSizeHandler>().decreaseFont();
},
icon: Icon(Icons.arrow_downward),
),
],
title: Text(
"DetailsPage",
style: GoogleFonts.roboto(),
),
),
body: SafeArea(
child: SingleChildScrollView(
child: Card(
child: Container(
padding: EdgeInsets.fromLTRB(5, 5, 5, 15),
child: AutoSizeText(
"MyTexts",
textAlign: TextAlign.justify,
style: GoogleFonts.openSans(
fontSize:
context.watch<FontSizeHandler>().fontSize.toDouble(),
),
),
),
),
),
),
);
}
}
So the problem here is I am getting this error message
Could not find the correct Provider This likely
happens because you used a BuildContext that does not include the
provider
Is this error is due to I used Navigator.pushAndRemoveUntil?
Though I have ChangeNotifierProvider at top of hierarchy why is it throwing error?
How to solve this?
FontSizeHandler.dart
class FontSizeHandler with ChangeNotifier {
int fontSize = 15;
void increaseFont() {
fontSize = fontSize + 2;
notifyListeners();
}
void decreaseFont() {
fontSize = fontSize - 2;
notifyListeners();
}
}
Solved: The Problem Was With Importing Wrong ChangeNotifier class. Never Trust autoimport again
Updated Answer
For reference, the issue was caused by an erroneous import as below:
import 'file:///.../fontchangehandler.dart'; // import 'package:.../fontchangehandler.dart';
Original Answer
Unfortunately I was unable to reproduce the error that you are experiencing using the provided code. If you could provide the rest of the code from the DetailsPage class then that might help to further diagnose the error. I was able to get the example below working which hopefully you might find useful:
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider<FontSizeHandler>(
create: (context) {
return FontSizeHandler();
},
),
],
child: MyApp(),
),
);
}
class FontSizeHandler extends ChangeNotifier {
int _fontSize = 15;
int get fontSize => _fontSize;
void increaseFont() {
_fontSize = _fontSize + 2;
notifyListeners();
}
void decreaseFont() {
_fontSize = _fontSize - 2;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SplashScreen(),
);
}
}
class SplashScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () async {
await Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return DetailsPage();
},
),
(route) => false,
);
},
child: Text('GO TO DETAILS'),
),
),
);
}
}
class DetailsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(
icon: Icon(Icons.arrow_upward),
onPressed: () {
context.read<FontSizeHandler>().increaseFont();
},
),
IconButton(
onPressed: () {
context.read<FontSizeHandler>().decreaseFont();
},
icon: Icon(Icons.arrow_downward),
),
],
title: Text(
"DetailsPage",
style: GoogleFonts.roboto(),
),
),
body: SafeArea(
child: SingleChildScrollView(
child: Card(
child: Container(
padding: EdgeInsets.fromLTRB(5, 5, 5, 15),
child: AutoSizeText(
"MyTexts",
textAlign: TextAlign.justify,
style: GoogleFonts.openSans(
fontSize: context.watch<FontSizeHandler>().fontSize.toDouble(),
),
),
),
),
),
),
);
}
}

Flutter: Card() / Firebase - (Beginner problem)

I am new in Flutter. I have a problem with a small project. I collect data from a Firebase DB and adding it into a listTile. I am trying to add 2 static ListTile but they are added to the homescreen for every loop. I can't get a out of the loop - Any one there are willing to help.
The "Datablade" and "Vejledning" listtile are added for every loop
'''
import 'package:flutter/material.dart';
import 'package:flutter_app/services/auth.dart';
import 'package:flutter_app/services/database.dart';
import 'package:provider/provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
//import 'package:flutter_app/screens/home/brew_list.dart';
import 'package:flutter_app/models/brew.dart';
class Home extends StatelessWidget {
final AuthService _auth = AuthService();
#override
Widget build(BuildContext context) {
return StreamProvider<List<Brew>>.value(
value: DatabaseService().filer,
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text('Arbejdsmiljø'),
backgroundColor: Colors.orange[400],
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
icon: Icon(Icons.person),
label: Text('Log ud'),
onPressed: () async {
await _auth.signOut();
},
),
],
),
body:
ListPage(),
),
);
//ListPage(),
}
}
class ListPage extends StatefulWidget {
#override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
Future _data;
Future getPosts() async{
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection('filer').getDocuments();
return qn.documents;
}
navigateToDetail(DocumentSnapshot post) {
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailPage(post: post,)));
}
void initState() {
super.initState();
_data = getPosts();
}
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: _data,
builder: (_, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: Text('Loading'),
);
} else {
return ListView.builder(
//return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index){
return Card(
child: Column(
children: <Widget> [
ListTile(
leading: Icon(Icons.star, color: Colors.orange, size: 26.0),
onTap: () => navigateToDetail(snapshot.data[index]),
title: Text('Datablade'),
trailing: Icon(Icons.keyboard_arrow_right),
),
Divider(color: Colors.orange,indent: 16.0),
ListTile(
title: new Center(child: new Text("Vejledninger",
style: new TextStyle(
color: Colors.orange[400],
fontWeight: FontWeight.w500, fontSize: 25.0),)),
),
ListTile(
onTap: () => navigateToDetail(snapshot.data[index]),
title: Text(snapshot.data[index].data["name"]),
leading: CircleAvatar(
backgroundColor: Colors.orange[400],
foregroundColor: Colors.white,
child: Text(snapshot.data[index].data["name"][0])
)
)
],
),
);
//return ListTile(
//title: Text(snapshot.data[index].data["name"]),
//onTap: () => navigateToDetail(snapshot.data[index]),
//);
});
}
}),
);
}
}
class DetailPage extends StatefulWidget {
final DocumentSnapshot post;
DetailPage ({this.post});
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.post.data["name"]),
),
body: Container(
child: Card(
child: ListTile(
title: Text(widget.post.data["name"]),
subtitle: Text(widget.post.data['beskrivelse']),
),
),
),
);
}
}
'''