Flutter
...
Data Objects
Типы данных Parse в Flutter: работа с JSON данными
18 мин
этот гид вводит типы данных parse в flutter, используя примеры из файла main dart хранение данных parse основано на parseobject, который содержит пары ключ значение совместимых с json данных, что делает его безсхемным это означает, что вы можете хранить любые данные, не определяя схему заранее вы можете установить любые пары ключ значение, которые хотите, и наш бэкенд будет их хранить например, предположим, что вы отслеживаете высокие баллы для игры один объект parse может содержать мы рекомендуем вам nameyourclasseslikethis и nameyourkeyslikethis для повышения читаемости и поддерживаемости кода понимание нашего приложения чтобы лучше понять back4app, давайте рассмотрим примеры кода операций parse в приложении flutter с основными поддерживаемыми типами данных этот гид не будет объяснять код приложения flutter, так как основное внимание уделяется использованию parse с flutter предварительные требования чтобы завершить этот учебник, вам потребуется android studio https //developer android com/studio или установленный vs code (с плагинами dart и flutter) приложение создано на back4app примечание следуйте учебнику по созданию нового parse app чтобы узнать, как создать parse app на back4app flutter приложение, подключенное к back4app примечание следуйте установите parse sdk в проект flutter чтобы создать проект flutter, подключенный к back4app устройство (или виртуальное устройство), работающее на android или ios 1 работа с объектами parse каждый parseobject имеет имя класса (например, gamepoint), используемое для различения различных типов данных вот как вы можете создать и сохранить новый объект parse с типами данных строка , целое число , & логическое значение final gamepoint = parseobject('gamepoint') set('score', 1337) set('playername', 'sean plott') set('cheatmode', false); await gamepoint save(); чтобы запросить новый объект parse и получить типы данных final querybuilder\<parseobject> querygamepoints = querybuilder\<parseobject>(parseobject('gamepoint')); final parseresponse response = await querygamepoints query(); if (response success && response results != null) { for (var gamepoint in response results!) { final score = gamepoint get\<int>('score'); final playername = gamepoint get\<string>('playername'); final cheatmode = gamepoint get\<bool>('cheatmode'); } 2 счетчики вы можете увеличить или уменьшить целочисленное поле в parseobject, используя метод set() однако это неэффективно и может привести к проблемам, если несколько клиентов пытаются обновить один и тот же счетчик parse предоставляет два метода, которые автоматически увеличивают и уменьшают любое числовое поле для хранения данных типа счетчика setincrement() setdecrement() обновление для увеличения значения счетчика int будет записано как final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' setincrement('intfield', 1); await gamepoint save(); чтобы уменьшить final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' setdecrement('intfield', 1); await gamepoint save(); использование setincrement() и setdecrement() с вызовом save() позволяет вам обновить значение как часть более крупной операции сохранения, где вы можете изменять несколько полей это лучше, чтобы избежать лишних сетевых запросов 3 списки parse предоставляет методы для работы с данными списков, включая setadd , setaddunique , setremove , и их соответствующие all версии список \["a","b","c"] 3 1 setadd final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' setadd('liststringfield', 'd'); await gamepoint save(); результат \["a","b","c","d"] 3 2 setaddall final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' setaddall('liststringfield', \['e','f']); await gamepoint save(); результат \["a","b","c","d","e","f"] setaddall не добавляет дублирующиеся элементы, если список уже их содержит 3 3 setaddunique final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' setaddunique('liststringfield', \['a', 'e', 'g']); await gamepoint save(); результат \["a","b","c","d","e","f","g"] 3 4 setremove final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' setremove('liststringfield', 'd'); await gamepoint save(); результат \["a","b","c","e","f","g"] 4 удалить поле из parseobject вы можете удалить одно поле из объекта, используя операцию unset final gamepoint = parseobject('gamepoint') objectid = 'yourobjectid' unset("liststringfield"); 5 файлы parsefile позволяет вам хранить и извлекать файлы приложений в облаке ниже приведен базовый пример загрузки локального изображения в back4app // create parsefile object final parsefile = parsefile(file(image path)); final response = await parsefile save(); if (response success) { print('file uploaded successfully ${parsefile url}'); // save this file in an object final gamepoint = parseobject('gamepoint') set('title', 'post with an image') set('imagefile', parsefile); // saving the file as a field final postresponse = await gamepoint save(); } этот фрагмент кода показывает, как извлечь файл изображения final query = querybuilder\<parseobject>(parseobject('gamepoint')); final response = await query query(); if (response success && response results != null) { for (var gamepoint in response results!) { parsefilebase? varfile = gamepoint get\<parsefilebase>('file'); if (varfile != null) { final fileurl = varfile url; print('game title image url $fileurl'); } else { print('no file found for this object '); } } } начиная с parse server 5 2 3 есть изменения, которые могут вызвать ошибки при попытке загрузить файлы следуйте этому руководству https //www back4app com/docs/platform/parse server version#o cil чтобы исправить любые проблемы с загрузкой, с которыми вы можете столкнуться позднее руководство дополнительно обсудит и покажет шаблоны о том, как сохранять и отображать файлы с помощью parse https //www back4app com/docs/flutter/parse sdk/flutter save file 6 geopoint parse позволяет вам ассоциировать реальные координаты широты и долготы с объектом с его типом данных geopoint добавление parsegeopoint к parseobject позволит запросам учитывать близость объекта к контрольной точке пример // request permission here and get the current location the `position` object // create geopoint object final geopoint = parsegeopoint(latitude position latitude, longitude position longitude); // create an object with the geopoint final gamepoint = parseobject('gamepoint') set('name', 'current location') set('location', geopoint); // save the geopoint in a field final response = await gamepoint save(); вы можете найти отдельный пример сохранения местоположения geopoint в этом main dart https //github com/templates back4app/flutter datatypes/blob/18c4c372eb49c1cd950eeaac355564134a5e70c4/lib/src/main dart файле полный пример приложения этот пример демонстрирует, как создайте, удалите и обновите объект gamepoint обрабатывайте различные типы данных, включая строки, числа с плавающей запятой, логические значения, файлы, геоточки, списки и даты на ios вам нужно будет предоставить вашему симулятору необходимые разрешения ios предоставляет руководство для разработчиков по защищенным ресурсам примерные приложения в этом руководстве потребуют разрешение на использование местоположения https //developer apple com/documentation/bundleresources/information property list/nslocationwheninuseusagedescription и разрешение на доступ к фотографиям https //developer apple com/documentation/bundleresources/information property list/nsphotolibraryaddusagedescription ключи разрешений, найденные в этом руководстве, чтобы работать добавьте ключи в файл info plist , расположенный по адресу /ios/runner/info plist вы можете пропустить вышеуказанный шаг, если запускаете свое приложение flutter в вебе import 'dart\ async'; import 'package\ flutter/material dart'; import 'package\ parse server sdk flutter/parse server sdk flutter dart'; import 'package\ image picker/image picker dart'; import 'package\ flutter/foundation dart'; import 'dart\ io'; void main() async { widgetsflutterbinding ensureinitialized(); const keyparseapplicationid = 'your application id'; const keyparseclientkey = 'your client key'; const keyparseserverurl = 'https //parseapi back4app com'; await parse() initialize( keyparseapplicationid, keyparseserverurl, clientkey keyparseclientkey, autosendsessionid true, debug true, ); runapp(const myapp()); } class myapp extends statelesswidget { const myapp({super key}); @override widget build(buildcontext context) { return materialapp( title 'datatypes', theme themedata( primaryswatch colors blue, ), home const myhomepage(), ); } } class myhomepage extends statefulwidget { const myhomepage({super key}); @override myhomepagestate createstate() => myhomepagestate(); } class myhomepagestate extends state\<myhomepage> { final texteditingcontroller stringcontroller = texteditingcontroller(); final texteditingcontroller doublecontroller = texteditingcontroller(); final texteditingcontroller liststringcontroller = texteditingcontroller(); final texteditingcontroller listintcontroller = texteditingcontroller(); final texteditingcontroller pointercontroller = texteditingcontroller(); final texteditingcontroller objectidcontroller = texteditingcontroller(); final texteditingcontroller uniquevaluecontroller = texteditingcontroller(); final texteditingcontroller removevaluecontroller = texteditingcontroller(); bool boolvalue = false; datetime selecteddate = datetime now(); final string responsemessage = ''; xfile? pickedfile; bool isloading = false; @override widget build(buildcontext context) { return scaffold( appbar appbar( title const text('datatypes'), ), body padding( padding const edgeinsets all(16 0), child singlechildscrollview( child column( crossaxisalignment crossaxisalignment start, children \<widget>\[ buildtextfield('string field', stringcontroller), const sizedbox(height 20), buildtextfield('double field', doublecontroller, isnumeric true), const sizedbox(height 20), buildswitch('bool field', boolvalue, (value) { setstate(() { boolvalue = value; }); }), const sizedbox(height 20), textbutton( onpressed () => selectdate(context), child text( 'select date ${ selecteddate tolocal()}' split(' ')\[0]), ), const sizedbox(height 20), buildtextfield( 'list string field (comma separated)', liststringcontroller), const sizedbox(height 20), buildtextfield( 'list int field (comma separated)', listintcontroller), const sizedbox(height 20), buildtextfield('pointer field (object id)', pointercontroller), const sizedbox(height 20), buildimagepicker(), const sizedbox(height 20), elevatedbutton( onpressed savedata, child const text('save'), ), const sizedbox(height 20), elevatedbutton( onpressed () async { await deletedata( objectidcontroller text); // await dummyfunction(); }, child const text('delete'), ), const sizedbox(height 20), buildtextfield( 'object id to delete or update', objectidcontroller), elevatedbutton( onpressed () async { await updatedata( objectidcontroller text); }, child const text('update'), ), const sizedbox(height 20), buildincrementdecrementbuttons(), const sizedbox(height 20), buildlistoperations(), const sizedbox(height 20), text( responsemessage), ], ), ), ), ); } widget buildtextfield(string label, texteditingcontroller controller, {bool isnumeric = false}) { return textfield( controller controller, keyboardtype isnumeric ? textinputtype number textinputtype text, decoration inputdecoration( border const outlineinputborder(), labeltext label, ), ); } widget buildswitch(string label, bool value, function(bool) onchanged) { return switchlisttile( title text(label), value value, onchanged onchanged, ); } widget buildimagepicker() { return gesturedetector( child pickedfile != null ? container( width 250, height 250, decoration boxdecoration(border border all(color colors blue)), child kisweb ? image network(pickedfile! path) image file(file(pickedfile! path)), ) container( width 250, height 250, decoration boxdecoration(border border all(color colors blue)), child const center( child text('click here to pick image from gamepoint')), ), ontap () async { final picker = imagepicker(); xfile? image = (await picker pickimage(source imagesource gallery)); if (image != null) { setstate(() { pickedfile = image; }); } }, ); } widget buildincrementdecrementbuttons() { return row( mainaxisalignment mainaxisalignment spacebetween, children \[ expanded( child elevatedbutton( onpressed () async { await incrementfield( objectidcontroller text); }, child const text('+ double'), ), ), const sizedbox(width 10), // adding some space between buttons expanded( child elevatedbutton( onpressed () async { await decrementfield( objectidcontroller text); }, child const text(' double'), ), ), ], ); } widget buildlistoperations() { return column( children \[ buildtextfield('unique value to add', uniquevaluecontroller), elevatedbutton( onpressed () async { await adduniquetolist( objectidcontroller text, uniquevaluecontroller text); }, child const text('add unique'), ), const sizedbox(height 20), buildtextfield('value to remove', removevaluecontroller), elevatedbutton( onpressed () async { await removefromlist( objectidcontroller text, removevaluecontroller text); }, child const text('remove'), ), ], ); } future\<void> savedata() async { //parse values string stringvalue = stringcontroller text; list\<string> liststringvalue = liststringcontroller text isnotempty ? liststringcontroller text split(',') // split by comma map((e) => e trim()) // remove any surrounding whitespace from each element tolist() \[]; list\<int> listtintvalue = listintcontroller text isnotempty ? listintcontroller text split(',') // split by comma map( (e) => int parse(e trim())) // convert each string to an integer tolist() \[]; double? doublevalue; if ( doublecontroller text isnotempty) { doublevalue = double parse( doublecontroller text); } bool boolvalue = boolvalue; datetime selecteddate = selecteddate; final gamepoint = parseobject('gamepoint') set('bool', boolvalue) set('date', selecteddate); if (stringvalue isnotempty) gamepoint set('string', stringvalue); if (doublevalue != null) gamepoint set('double', doublevalue); if (liststringvalue isnotempty) { gamepoint setaddall('liststring', liststringvalue); } if (listtintvalue isnotempty) gamepoint setaddall('listint', listtintvalue); if ( pointercontroller text isnotempty) { gamepoint set('pointer', parsepointer( pointercontroller text)); } if (pickedfile != null) { setstate(() { isloading = true; }); parsefilebase? parsefile; if (kisweb) { parsefile = parsewebfile(await pickedfile! readasbytes(), name 'file jpg'); } else { parsefile = parsefile(file(pickedfile! path)); } await parsefile save(); gamepoint set('file', parsefile); } var apiresponse = await gamepoint save(); if (apiresponse success && apiresponse results != null) { setstate(() { isloading = false; pickedfile = null; }); scaffoldmessenger of(context) removecurrentsnackbar() showsnackbar(const snackbar( content text( 'file saved successfully on back4app', style textstyle(color colors white), ), duration duration(seconds 3), backgroundcolor colors blue, )); } else { print("this is your request error ${apiresponse error}"); } } future\<void> updatedata(string objectid) async { final gamepoint = parseobject('gamepoint') objectid = objectid; // update fields with new values if ( stringcontroller text isnotempty) { gamepoint set('string', stringcontroller text); } if ( doublecontroller text isnotempty) { gamepoint set('double', double parse( doublecontroller text)); } gamepoint set('bool', boolvalue); gamepoint set('date', selecteddate); if ( liststringcontroller text isnotempty) { list\<string> liststringvalue = liststringcontroller text split(',') map((e) => e trim()) tolist(); gamepoint setaddall('liststring', liststringvalue); } if ( listintcontroller text isnotempty) { list\<int> listintvalue = listintcontroller text split(',') map((e) => int parse(e trim())) tolist(); gamepoint setaddunique('listint', listintvalue); } if ( pointercontroller text isnotempty) { gamepoint set('pointer', parsepointer( pointercontroller text)); } if (pickedfile != null) { parsefilebase? parsefile; if (kisweb) { parsefile = parsewebfile(await pickedfile! readasbytes(), name 'file jpg'); } else { parsefile = parsefile(file(pickedfile! path)); } await parsefile save(); gamepoint set('file', parsefile); } // save the updated object var response = await gamepoint save(); if (response success) { scaffoldmessenger of(context) showsnackbar( const snackbar(content text('data updated successfully!')), ); } else { scaffoldmessenger of(context) showsnackbar( snackbar( content text('error updating data ${response error! message}')), ); } } future\<void> incrementfield(string objectid) async { final gamepoint = parseobject('gamepoint') objectid = objectid setincrement('double', 1); await gamepoint save(); } future\<void> decrementfield(string objectid) async { final gamepoint = parseobject('gamepoint') objectid = objectid setdecrement('double', 1); await gamepoint save(); } future\<void> adduniquetolist(string objectid, string value) async { final gamepoint = parseobject('gamepoint') objectid = objectid setaddunique('liststring', value); await gamepoint save(); } future\<void> removefromlist(string objectid, string value) async { final gamepoint = parseobject('gamepoint') objectid = objectid setremove('liststring', value); await gamepoint save(); } future\<void> selectdate(buildcontext context) async { final datetime? picked = await showdatepicker( context context, initialdate selecteddate, firstdate datetime(2000), lastdate datetime(2101), ); if (picked != null && picked != selecteddate) { setstate(() { selecteddate = picked; }); } } future\<void> deletedata(string objectid) async { // delete data logic final parseobject = parseobject('gamepoint') objectid = objectid unset("liststring"); await parseobject save(); } // convert pointer field to a suitable format parseobject parsepointer(string objectid) { // there should be an existing game class with objects final pointer = parseobject('game') objectid = objectid; return pointer; } } этот код инициализирует parse sdk в flutter, настраивает основное приложение и отображает простую домашнюю страницу с заголовком заключение в этом руководстве вы узнали о parseobjects и различных типах данных, доступных в parse вы также узнали, как обрабатывать операции, такие как сохранение и извлечение типов данных из вашего бэкенда back4app