Я создаю свое первое большое приложение во Flutter, и первое, где мне нужно State Management, поэтому я обратился к Provider - рекомендованному пакету для State Management. Однако у меня возникают некоторые проблемы, когда я объявляю своих провайдеров в файле main.dart и вниз по дереву. Я хочу внести изменения и взаимодействовать с одним из провайдеров, но независимо от того, какое решение я пробую, я получаю одну и ту же ошибку: «Пробовал прослушать значение, предоставляемое поставщиком, из-за пределов дерева виджетов. " Я получаю эту ошибку, даже если, по словам инспектора флаттера, виджет, из которого я пытаюсь внести изменения в провайдера, находится внутри дерева виджетов (экран «Домашний экран» находится там, где я обновляю провайдера).
Ниже я также делюсь своим кодом: main.dart:
import 'package:flutter/material.dart';
import 'package:tic_tac_2/screens/welcome_screen.dart';
import 'package:provider/provider.dart';
import 'package:tic_tac_2/models/restaurants_data.dart';
import 'package:tic_tac_2/models/promotions_data.dart';
import 'package:tic_tac_2/models/user.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<User>(create: (context) => User(),),
ChangeNotifierProvider<RestaurantsData>(create: (context) => RestaurantsData(),),
ChangeNotifierProvider<PromotionsData>(create: (context) => PromotionsData(),),
],
child: MaterialApp(
title: 'Tic Tac',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WelcomeScreen(),
),
);
}
}
welcome_screen.dart:
import 'package:flutter/material.dart';
import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:tic_tac_2/components/rounded_button.dart';
import 'login_screen.dart';
import 'register_screen.dart';
class WelcomeScreen extends StatelessWidget {
static const String id = 'welcome_screen';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xff000080),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
children: <Widget>[
Hero(
tag: 'logo',
child: Container(
child: Image.asset('images/pin.png'),
height: 60.0,
),
),
TypewriterAnimatedTextKit(
text: ['Tic Tac'],
textStyle: TextStyle(
fontWeight: FontWeight.w900,
fontSize: 45.0,
color: Colors.white
),
),
],
),
SizedBox(
height: 48.0,
),
RoundedButton(
title: 'Entrar',
colour: Colors.lightBlueAccent,
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => LoginScreen()));
//Navigator.pushNamed(context, LoginScreen.id);
},
),
RoundedButton(
title: 'Registro',
colour: Colors.blueAccent,
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => RegistrationScreen()));
//Navigator.pushNamed(context, RegistrationScreen.id);
},
),
],
),
),
);
}
}
login_screen. dart:
import 'package:flutter/material.dart';
import 'package:tic_tac_2/components/rounded_button.dart';
import 'package:tic_tac_2/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:modal_progress_hud/modal_progress_hud.dart';
import 'home_screen.dart';
import 'package:tic_tac_2/models/user.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'package:email_validator/email_validator.dart';
final _firestore = Firestore.instance;
class LoginScreen extends StatefulWidget {
static const String id = 'login_screen';
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
String email;
String password;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/pin.png'),
),
),
),
SizedBox(
height: 48.0,
),
TextFormField(
validator: (val) => !EmailValidator.validate(val, true)
? 'Correo inválido'
: null,
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.center,
onChanged: (value) {
email = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Escribe tu correo'),
),
SizedBox(
height: 8.0,
),
TextFormField(
validator: (val) =>
val.length < 6 ? 'La contraseña es muy corta' : null,
obscureText: true,
textAlign: TextAlign.center,
onChanged: (value) {
password = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Escribe tu contraseña'),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Entrar',
colour: Colors.lightBlueAccent,
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() {
showSpinner = true;
});
try {
final user = await _auth.signInWithEmailAndPassword(
email: email, password: password);
if (user != null) {
return _firestore
.collection('user')
.document(user.user.uid)
.get()
.then((DocumentSnapshot ds) {
User localUser = User(
uid: user.user.uid,
email: email,
role: ds.data['role']);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
user: user.user,
newUser: localUser,
)));
});
}
setState(() {
showSpinner = false;
});
} catch (e) {
setState(() {
showSpinner = false;
});
Alert(
context: context,
title: "Error en el registro",
desc: e)
.show();
print(e);
}
}
},
),
],
),
),
),
),
);
}
}
home_screen.dart:
import 'package:tic_tac_2/models/user.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'package:tic_tac_2/models/restaurants_data.dart';
import 'package:provider/provider.dart';
import 'package:tic_tac_2/models/promotions_data.dart';
import 'package:tic_tac_2/widgets/RestaurantList.dart';
import 'package:geolocator/geolocator.dart';
Geoflutterfire geo = Geoflutterfire();
FirebaseUser loggedInUser;
User localUser;
class HomeScreen extends StatefulWidget {
final FirebaseUser user;
final User newUser;
const HomeScreen({Key key, this.user, this.newUser}) : super(key: key);
static const String id = 'home_screen';
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _firestore = Firestore.instance;
GoogleMapController mapController;
var pos;
Stream<dynamic> query;
StreamSubscription subscription;
@override
void dispose() {
// TODO: implement dispose
super.dispose();
subscription.cancel();
}
@override
void initState() {
// TODO: implement initState
super.initState();
if (localUser == null) {
localUser = widget.newUser;
loggedInUser = widget.user;
}
}
@override
Widget build(BuildContext context) {
void _getCurrentLocation(BuildContext context) async {
try {
Position position = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
print('lat');
print(position.latitude);
print('lng');
print(position.longitude);
final QuerySnapshot restaurants = await _firestore.collection('restaurants').getDocuments();
for(var restaurant in restaurants.documents) {
print(restaurant);
Provider.of<RestaurantsData>(context).addRestaurant(
name: restaurant.data['name'],
owner: restaurant.data['owner'],
location: restaurant.data['location'],
uid: restaurant.data['uid'],
);
}
} catch (e) {
print(e);
}
}
WidgetsBinding.instance.addPostFrameCallback((_) => _getCurrentLocation(context));
print(Provider.of<RestaurantsData>(context).restaurants);
return Scaffold(
backgroundColor: Color(0xff000080),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.only(
top: 60.0,
bottom: 30.0,
left: 30.0,
right: 30.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
CircleAvatar(
child: Icon(
Icons.list,
size: 30.0,
color: Color(0xff000080),
),
backgroundColor: Colors.white,
radius: 30.0,
),
SizedBox(
height: 10.0,
),
Text(
'Tic Tac',
style: TextStyle(
fontSize: 50.0,
color: Colors.white,
fontWeight: FontWeight.w700,
),
),
Text(
'Restaurantes',
style: TextStyle(color: Colors.white, fontSize: 18.0),
)
],
),
),
Expanded(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0),
),
),
child:
Provider.of<RestaurantsData>(context).restaurants.length > 0
? RestaurantList()
: Container(),
),
),
],
),
);
}
}
Насколько я могу судить, причиной проблемы в файле home_screen является " getCurrentLocation (BuildContext) context) {} "функция и как и когда я ее вызываю. Я попытался превратить все в statelessWidgets, вызывая функцию getLocation без "WidgetsBinding.instance.addPostFrameCallback (() => _getCurrentLocation (context));" линия. Я пытался не передавать контекст функции в числе других решений, которые я пробовал.
Я очень ценю вашу помощь и хотел бы поблагодарить вас заранее. Если у вас есть какие-либо сомнения относительно кода, я буду более чем рад ответить на все из них.