Уточнение о провайдере во Flutter - Как сохранить данные
Я новичок в Flutter
и я трачу некоторое время на скороговорку поставщика, я парень PHP, поэтому у меня есть базовые знания о том, как это работает, особенно в Laravel
фреймворк.
Для меня провайдер - это то, что должно управлять данными, извлекать их из БД, управлять сохранением рекламы.
Насколько я понимаю, во флаттере концепция такая же, но следовать документации довольно сложно.
Я создал простое приложение, которое вызывает REST API, и теперь я хотел бы добавить ответ своему провайдеру, чтобы использовать данные на всех моих страницах и виджетах.
Вот мой код.
В splash.dart
файл, который показывает экран-заставку моему пользователю и вызывает внешнюю конечную точку, здесь я хотел бы получить свои данные и добавить их к поставщику.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/maison.dart';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
class SplashApp extends StatefulWidget {
final VoidCallback onInitializationComplete;
const SplashApp({
Key key,
@required this.onInitializationComplete,
}) : super(key: key);
@override
_SplashAppState createState() => _SplashAppState();
}
class _SplashAppState extends State<SplashApp> {
@override
void initState() {
super.initState();
_initializeAsyncDependencies();
}
Future<void> _initializeAsyncDependencies() async {
var url = 'https://run.mocky.io/v3/95237af1-b13f-4756-b308-56c9aac93c7e';
// Await the http get response, then decode the json-formatted response.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body);
var data = jsonResponse['data'];
var name = data[0]['name'];
print('Name: $name.');
} else {
print('Request failed with status: ${response.statusCode}.');
}
// >>> initialize async dependencies <<<
// >>> register favorite dependency manager <<<
// >>> reap benefits <<<
Future.delayed(
Duration(milliseconds: 3000),
() => widget.onInitializationComplete(),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Splash Screen',
theme: ThemeData(
accentColor: Colors.black,
),
home: _buildBody(),
);
}
Widget _buildBody() {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: Color(0xFFDDCDC8),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 2,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.white,
radius: 80.0,
child: Icon(
Icons.access_time,
color: Colors.black,
size: 80.0,
),
),
Padding(
padding: EdgeInsets.only(
top: 10.0,
),
),
Text(
'Title',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 26.0,
),
),
SizedBox(
height: 16.0,
),
Text(
'Subtitle',
style: TextStyle(
fontSize: 16.0,
),
),
],
),
),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Padding(
padding: EdgeInsets.only(top: 20.0),
),
Text('Caricamento'),
],
),
),
],
)
],
),
);
}
}
Вот мой main.dart
файл, здесь я инициализирую свои вкладки и еще кое-что. Я добавил тудаChangeNotifyer
потому что здесь мне нужны данные, может не то место?
import 'package:testapp/localization/app_localization.dart';
import 'package:testapp/pages/home.dart';
import 'package:testapp/pages/maison.dart';
import 'package:testapp/pages/map.dart';
import 'package:testapp/splash.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:testapp/providers/maisons_provider.dart';
void main() {
LicenseRegistry.addLicense(() async* {
final license = await rootBundle.loadString('google_fonts/OFL.txt');
yield LicenseEntryWithLineBreaks(['google_fonts'], license);
});
runApp(
SplashApp(
key: UniqueKey(),
onInitializationComplete: () => runMainApp(),
),
);
}
void runMainApp() {
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return ChangeNotifierProvider(
builder: (ctx) => MaisonsProvider(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Title',
theme: ThemeData(
primaryColor: Colors.black,
scaffoldBackgroundColor: Color(0xFFDDCDC8),
visualDensity: VisualDensity.adaptivePlatformDensity,
textTheme: GoogleFonts.montserratTextTheme(textTheme).copyWith(
headline1: GoogleFonts.montserrat(
textStyle: textTheme.headline1,
fontSize: 30,
fontWeight: FontWeight.w600,
color: Colors.black,
),
headline2: GoogleFonts.montserrat(
textStyle: textTheme.headline1,
fontSize: 22,
fontWeight: FontWeight.w500,
color: Colors.black,
),
bodyText1: GoogleFonts.montserrat(
textStyle: textTheme.bodyText1,
color: Colors.black,
fontWeight: FontWeight.w400,
fontSize: 19,
),
),
),
home: BottomBar(),
routes: {
MaisonPage.routeName: (ctx) => MaisonPage(),
},
supportedLocales: [
Locale('en', 'US'),
Locale('it', 'IT'),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
),
);
}
}
class BottomBar extends StatefulWidget {
@override
_BottomBarState createState() => _BottomBarState();
}
class _BottomBarState extends State<BottomBar> {
int _currentIndex = 0;
final List<Widget> _children = [
HomePage(),
MapPage(),
];
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _children[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Color(0xFFDDCDC8),
currentIndex: _currentIndex,
onTap: onTabTapped,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.list),
title: Text(
AppLocalizations.of(context).translate('tab_maisons'),
),
),
BottomNavigationBarItem(
icon: Icon(
Icons.map,
),
title: Text(
AppLocalizations.of(context).translate('tab_map'),
),
),
],
),
);
}
}
Это моя модель от Maison.
import 'package:testapp/models/point.dart';
import 'package:flutter/material.dart';
class Maison {
final String id;
final String name;
final String imageUrl;
final String price;
final Point coordinates;
Maison({
@required this.id,
@required this.name,
@required this.price,
@required this.imageUrl,
@required this.coordinates,
});
}
А вот и провайдер:
import 'package:testapp/models/point.dart';
import 'package:flutter/material.dart';
import '../models/maison.dart';
class MaisonsProvider with ChangeNotifier {
// Questa lista è privata, non può essere recuperata dall'esterno, serve un getter
List<Maison> _items = [
Maison(
id: '1',
name: 'Prova 1',
price: '60€',
imageUrl:
'https://images.unsplash.com/photo-1495562569060-2eec283d3391?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80',
coordinates: Point(
lat: 45.4640976,
lng: 9.1897378,
),
),
Maison(
id: '2',
name: 'Prova 2',
price: 'Free',
imageUrl:
'https://images.unsplash.com/photo-1582559934353-2e47140e7501?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1788&q=80',
coordinates: Point(
lat: 45.4640976,
lng: 10.1897378,
),
),
Maison(
id: '3',
name: 'Prova 3',
price: 'Free',
imageUrl:
'https://images.unsplash.com/photo-1553355617-f7342d67a95f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80',
coordinates: Point(
lat: 45.9,
lng: 10.1897378,
),
),
Maison(
id: '4',
name: 'Prova 4',
price: '40€',
imageUrl:
'https://images.unsplash.com/photo-1555141816-810dd5692b6a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1789&q=80',
coordinates: Point(
lat: 46.4640976,
lng: 9.30,
),
),
];
// Getter
// fa in modo che venga ritornata la lista di Maison,
// non è la lista originale ma una copia.
List<Maison> get items {
return [..._items];
}
// Aggiunge una nuova Maison alla lista.
// è importante passare dal provider così che sia lui a informare gli altri widget
// che c'è stato un cambiamento
void addMaison(Maison maison) {
_items.add(maison);
// Informa tutti i widget che sono interessati che c'è una nuova maison
// in questo modo i widget possono fare una rebuild e mostrare i dati corretti
notifyListeners();
}
}
Я добавил addMaison
метод, который должен быть ответственным за добавление нового Дома в мой _item
и геттер, чтобы получить его копию.
Я попытался запустить своего провайдера в splash.dart следующим образом:
final maisonsData = Provider.of<MaisonsProvider>(context);
Maison m = Maison(
id: '1',
name: 'Prova add',
imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png',
coordinates: {
lat: 23,
lng: 23,
}
);
maisonData.addMaison(m);
и я попытался добавить его в свой _initializeAsyncDependencies
но приложения продолжают показывать заставку и не могут продолжить.
Как я могу использовать провайдера в HTTP-запросе?
1 ответ
Поставщики - действительно хороший инструмент для обновления вашего дерева виджетов без вызова метода setstate, который он работает в части дерева.
класс ChangeNotifier:
в этом классе вы можете обрабатывать любую логику своего приложения, в вашем случае отправляет запрос, управляющий ответом данных.
Конструктор ChangeNotifier provider widget
:
здесь вы инициализируете свой класс уведомления об изменениях, он должен быть выше в дереве виджетов, поэтому дочерний элемент, зависящий от кода вашей логики (уведомитель об изменении), получает уведомление, когда выполняется некоторая логика.
Как прислушиваться к изменениям Consumer widget and provider.of<ChangeNotifierClass>(context)
:
эти виджеты слушают
notifyListeners()
вызовите внутри вашего класса уведомителя об изменениях, поэтому каждый раз, когда логика запускает весь дочерний виджет, который слушает ваш ChangeNotifier, будет запускаться метод сборки (так что это в основном похоже на вызов setstate).
Итак, для вашего вопроса вам просто нужно реализовать логику вашего запроса, создавая списки из данных json и прочего.... чем запускать notifylisteners для отображения и нового состояния ваших виджетов, которые интересуются этими данными.
Для меня было довольно сложно принять такую логику, но после того, как я сделал это однажды, я ясно увидел, что она хорошо сочетается с флаттером, и для получения дополнительной помощи вы можете проверить архитектуру блочного шаблона.
надеюсь, это помогло вам удачи.