diff --git a/README.md b/README.md index 192250ba4..cd73effc2 100644 --- a/README.md +++ b/README.md @@ -1,893 +1,21 @@ -![Parse Logo](https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png)![Flutter Logo](https://i2.wp.com/blog.openshift.com/wp-content/uploads/parse-server-logo-1.png?fit=200%2C200&ssl=1&resize=350%2C200) +![Parse Logo](https://i2.wp.com/blog.openshift.com/wp-content/uploads/parse-server-logo-1.png?fit=200%2C200&ssl=1&resize=350%2C200) +--- -## Parse For Flutter! -Hi, this is a Flutter plugin that allows communication with a Parse Server, (https://parseplatform.org) either hosted on your own server or another, like (http://Back4App.com). +This repository contains packages that allow communication with a Parse Server, +(https://parseplatform.org) either hosted on your own server or another, +like (http://Back4App.com). This is a work in progress and we are consistently updating it. Please let us know if you think anything needs changing/adding, and more than ever, please do join in on this project. (Even if it is just to improve our documentation) -## Join in! -Want to get involved? Join our Slack channel and help out! (http://flutter-parse-sdk.slack.com) +## Packages -## Getting Started -To install, either add to your pubspec.yaml -```yml -dependencies: - parse_server_sdk: ^1.0.28 -``` -or clone this repository and add to your project. As this is an early development with multiple contributors, it is probably best to download/clone and keep updating as an when a new feature is added. +These are the available packages in this repository. +| Plugin | Pub | explanation| +|--------|-----|------------| +| [parse_server_sdk](./packages/dart) | [![pub package](https://img.shields.io/pub/v/parse_server_sdk.svg)](https://pub.dev/packages/parse_server_sdk) | a dart package that lets you communicate with the parse server | +| [parse_server_sdk_flutter](./packages/dart) | [![pub package](https://img.shields.io/pub/v/parse_server_sdk_flutter.svg)](https://pub.dev/packages/parse_server_sdk_flutter) | a flutter package that lets you communicate with the parse server | -Once you have the library added to your project, upon first call to your app (Similar to what your application class would be) add the following... - -```dart -await Parse().initialize( - keyApplicationId, - keyParseServerUrl); -``` - -If you want to use secure storage or use the Flutter web/desktop SDK, please change to the below instance of CoreStorage as it has no dependencies on Flutter. - -**The `CoreStoreSembastImp` does not encrypt the data!** (Web is not safe anyway. Encrypt fields manually as needed.) -```dart - -await Parse().initialize( - keyParseApplicationId, - keyParseServerUrl, - coreStore: await CoreStoreSembastImp.getInstance()); -``` -It's possible to add other parameters to work with your instance of Parse Server:- - -```dart - await Parse().initialize( - keyApplicationId, - keyParseServerUrl, - masterKey: keyParseMasterKey, // Required for Back4App and others - clientKey: keyParseClientKey, // Required for some setups - debug: true, // When enabled, prints logs to console - liveQueryUrl: keyLiveQueryUrl, // Required if using LiveQuery - autoSendSessionId: true, // Required for authentication and ACL - securityContext: securityContext, // Again, required for some setups - coreStore: await CoreStoreSharedPrefsImp.getInstance()); // Local data storage method. Will use SharedPreferences instead of Sembast as an internal DB -``` - - -#### Early Web support -Currently this requires adding `X-Parse-Installation-Id` as an allowed header to parse-server. -When running directly via docker, set the env var `PARSE_SERVER_ALLOW_HEADERS=X-Parse-Installation-Id`. -When running via express, set [ParseServerOptions](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `allowHeaders: ['X-Parse-Installation-Id']`. - -Be aware that for web ParseInstallation does include app name, version or package identifier. - - -## Objects -You can create custom objects by calling: -```dart -var dietPlan = ParseObject('DietPlan') - ..set('Name', 'Ketogenic') - ..set('Fat', 65); -await dietPlan.save(); -``` -Or update existing object by its objectId by calling: -```dart -var dietPlan = ParseObject('DietPlan') - ..objectId = 'R5EonpUDWy' - ..set('Fat', 70); -await dietPlan.save(); -``` -Verify that the object has been successfully saved using -```dart -var response = await dietPlan.save(); -if (response.success) { - dietPlan = response.results.first; -} -``` -Types supported: - * String - * Double - * Int - * Boolean - * DateTime - * File - * Geopoint - * ParseObject/ParseUser (Pointer) - * Map - * List (all types supported) - -You then have the ability to do the following with that object: -The features available are:- - * Get - * GetAll - * Create - * Save - * Query - By object Id - * Delete - * Complex queries as shown above - * Pin - * Plenty more - * Counters - * Array Operators - -## Custom Objects -You can create your own `ParseObjects` or convert your existing objects into Parse Objects by doing the following: - -```dart -class DietPlan extends ParseObject implements ParseCloneable { - - DietPlan() : super(_keyTableName); - DietPlan.clone(): this(); - - /// Looks strangely hacky but due to Flutter not using reflection, we have to - /// mimic a clone - @override clone(Map map) => DietPlan.clone()..fromJson(map); - - static const String _keyTableName = 'Diet_Plans'; - static const String keyName = 'Name'; - - String get name => get(keyName); - set name(String name) => set(keyName, name); -} - -``` - -When receiving an `ParseObject` from the SDK, you can often provide an instance of your custom object as an copy object. -To always use your custom object class, you can register your subclass at the initialization of the SDK. -```dart -Parse().initialize( - ..., - registeredSubClassMap: { - 'Diet_Plans': () => DietPlan(), - }, - parseUserConstructor: (username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress), -); -``` -Additionally you can register `SubClasses` after the initialization of the SDK. -```dart -ParseCoreData().registerSubClass('Diet_Plans', () => DietPlan()); -ParseCoreData().registerUserSubClass((username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress)); -``` -Providing a `ParseObject` as described above should still work, even if you have registered a different `SubClass`. - -For custom file classes have a lock at [here](#File). - -## Add new values to objects -To add a variable to an object call and retrieve it, call - -```dart -dietPlan.set('RandomInt', 8); -var randomInt = dietPlan.get('RandomInt'); -``` - -## Save objects using pins -You can now save an object by calling `.pin()` on an instance of an object - -```dart -dietPlan.pin(); -``` - -and to retrieve it - -```dart -var dietPlan = DietPlan().fromPin('OBJECT ID OF OBJECT'); -``` - -## Storage -We now have 2 types of storage, secure and unsecure. We currently rely on 2 third party options: - -- SharedPreferences -- Sembast -Sembast offers secured storage, whilst SharePreferences wraps NSUserDefaults (on iOS) and SharedPreferences (on Android). - -The storage method is defined in the parameter __coreStore__ in Parse().initialize - -Check sample code for options - -## Increment Counter values in objects -Retrieve it, call - -```dart -var response = await dietPlan.increment("count", 1); - -``` -or using with save function - -```dart -dietPlan.setIncrement('count', 1); -dietPlan.setDecrement('count', 1); -var response = dietPlan.save() - -``` - -## Array Operator in objects -Retrieve it, call - -```dart -var response = await dietPlan.add("listKeywords", ["a", "a","d"]); - -var response = await dietPlan.addUnique("listKeywords", ["a", "a","d"]); - -var response = await dietPlan.remove("listKeywords", ["a"]); - -``` -or using with save function - -```dart -dietPlan.setAdd('listKeywords', ['a','a','d']); -dietPlan.setAddUnique('listKeywords', ['a','a','d']); -dietPlan.setRemove('listKeywords', ['a']); -var response = dietPlan.save() -``` - -## Queries -Once you have setup the project and initialised the instance, you can then retreive data from your server by calling: -```dart -var apiResponse = await ParseObject('ParseTableName').getAll(); - -if (apiResponse.success){ - for (var testObject in apiResponse.result) { - print(ApplicationConstants.APP_NAME + ": " + testObject.toString()); - } -} -``` -Or you can get an object by its objectId: - -```dart -var dietPlan = await DietPlan().getObject('R5EonpUDWy'); - -if (dietPlan.success) { - print(ApplicationConstants.keyAppName + ": " + (dietPlan.result as DietPlan).toString()); -} else { - print(ApplicationConstants.keyAppName + ": " + dietPlan.exception.message); -} -``` - -## Complex queries -You can create complex queries to really put your database to the test: - -```dart -var queryBuilder = QueryBuilder(DietPlan()) - ..startsWith(DietPlan.keyName, "Keto") - ..greaterThan(DietPlan.keyFat, 64) - ..lessThan(DietPlan.keyFat, 66) - ..equals(DietPlan.keyCarbs, 5); - -var response = await queryBuilder.query(); - -if (response.success) { - print(ApplicationConstants.keyAppName + ": " + ((response.results as List).first as DietPlan).toString()); -} else { - print(ApplicationConstants.keyAppName + ": " + response.exception.message); -} -``` - -if you want to find objects that match one of several queries, you can use __QueryBuilder.or__ method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do: -```dart -ParseObject playerObject = ParseObject("Player"); - -QueryBuilder lotsOfWins = - QueryBuilder(playerObject)) - ..whereGreaterThan('wins', 50); - -QueryBuilder fewWins = - QueryBuilder(playerObject) - ..whereLessThan('wins', 5); - -QueryBuilder mainQuery = QueryBuilder.or( - playerObject, - [lotsOfWins, fewWins], - ); - -var apiResponse = await mainQuery.query(); -``` - -The features available are:- - * Equals - * Contains - * LessThan - * LessThanOrEqualTo - * GreaterThan - * GreaterThanOrEqualTo - * NotEqualTo - * StartsWith - * EndsWith - * Exists - * Near - * WithinMiles - * WithinKilometers - * WithinRadians - * WithinGeoBox - * MatchesQuery - * DoesNotMatchQuery - * MatchesKeyInQuery - * DoesNotMatchKeyInQuery - * Regex - * Order - * Limit - * Skip - * Ascending - * Descending - * Plenty more! - -## Relational queries -If you want to retrieve objects where a field contains an object that matches another query, you can use the -__whereMatchesQuery__ condition. -For example, imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. -You can find comments on posts with images by doing: - -```dart -QueryBuilder queryPost = - QueryBuilder(ParseObject('Post')) - ..whereValueExists('image', true); - -QueryBuilder queryComment = - QueryBuilder(ParseObject('Comment')) - ..whereMatchesQuery('post', queryPost); - -var apiResponse = await queryComment.query(); -``` - -If you want to retrieve objects where a field contains an object that does not match another query, you can use the -__whereDoesNotMatchQuery__ condition. -Imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. -You can find comments on posts without images by doing: - -```dart -QueryBuilder queryPost = - QueryBuilder(ParseObject('Post')) - ..whereValueExists('image', true); - -QueryBuilder queryComment = - QueryBuilder(ParseObject('Comment')) - ..whereDoesNotMatchQuery('post', queryPost); - -var apiResponse = await queryComment.query(); -``` - -You can use the __whereMatchesKeyInQuery__ method to get objects where a key matches the value of a key in a set of objects resulting from another query. For example, if you have a class containing sports teams and you store a user’s hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like:: - -```dart -QueryBuilder teamQuery = - QueryBuilder(ParseObject('Team')) - ..whereGreaterThan('winPct', 0.5); - -QueryBuilder userQuery = - QueryBuilderParseUser.forQuery()) - ..whereMatchesKeyInQuery('hometown', 'city', teamQuery); - -var apiResponse = await userQuery.query(); -``` - -Conversely, to get objects where a key does not match the value of a key in a set of objects resulting from another query, use __whereDoesNotMatchKeyInQuery__. For example, to find users whose hometown teams have losing records: - -```dart -QueryBuilder teamQuery = - QueryBuilder(ParseObject('Team')) - ..whereGreaterThan('winPct', 0.5); - -QueryBuilder losingUserQuery = - QueryBuilderParseUser.forQuery()) - ..whereDoesNotMatchKeyInQuery('hometown', 'city', teamQuery); - -var apiResponse = await losingUserQuery.query(); -``` - -To filter rows based on objectId’s from pointers in a second table, you can use dot notation: -```dart -QueryBuilder rolesOfTypeX = - QueryBuilder(ParseObject('Role')) - ..whereEqualTo('type', 'x'); - -QueryBuilder groupsWithRoleX = - QueryBuilder(ParseObject('Group'))) - ..whereMatchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); - -var apiResponse = await groupsWithRoleX.query(); -``` - -## Counting Objects -If you only care about the number of games played by a particular player: - -```dart -QueryBuilder queryPlayers = - QueryBuilder(ParseObject('GameScore')) - ..whereEqualTo('playerName', 'Jonathan Walsh'); -var apiResponse = await queryPlayers.count(); -if (apiResponse.success && apiResponse.result != null) { - int countGames = apiResponse.count; -} -``` - -## Live Queries -This tool allows you to subscribe to a QueryBuilder you are interested in. Once subscribed, the server will notify clients -whenever a ParseObject that matches the QueryBuilder is created or updated, in real-time. - -Parse LiveQuery contains two parts, the LiveQuery server and the LiveQuery clients. In order to use live queries, you need -to set up both of them. - -The Parse Server configuration guide on the server is found here https://docs.parseplatform.org/parse-server/guide/#live-queries and is not part of this documentation. - -Initialize the Parse Live Query by entering the parameter liveQueryUrl in Parse().initialize: -```dart -Parse().initialize( - keyApplicationId, - keyParseServerUrl, - clientKey: keyParseClientKey, - debug: true, - liveQueryUrl: keyLiveQueryUrl, - autoSendSessionId: true); -``` - -Declare LiveQuery: -```dart -final LiveQuery liveQuery = LiveQuery(); -``` - -Set the QueryBuilder that will be monitored by LiveQuery: -```dart -QueryBuilder query = - QueryBuilder(ParseObject('TestAPI')) - ..whereEqualTo('intNumber', 1); -``` -__Create a subscription__ -You’ll get the LiveQuery events through this subscription. -The first time you call subscribe, we’ll try to open the WebSocket connection to the LiveQuery server for you. - -```dart -Subscription subscription = await liveQuery.client.subscribe(query); -``` - -__Event Handling__ -We define several types of events you’ll get through a subscription object: - -__Create event__ -When a new ParseObject is created and it fulfills the QueryBuilder you subscribe, you’ll get this event. -The object is the ParseObject which was created. -```dart -subscription.on(LiveQueryEvent.create, (value) { - print('*** CREATE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); -}); -``` - -__Update event__ -When an existing ParseObject which fulfills the QueryBuilder you subscribe is updated (The ParseObject fulfills the -QueryBuilder before and after changes), you’ll get this event. -The object is the ParseObject which was updated. Its content is the latest value of the ParseObject. -```dart -subscription.on(LiveQueryEvent.update, (value) { - print('*** UPDATE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); -}); -``` - -__Enter event__ -When an existing ParseObject’s old value does not fulfill the QueryBuilder but its new value fulfills the QueryBuilder, -you’ll get this event. The object is the ParseObject which enters the QueryBuilder. -Its content is the latest value of the ParseObject. -```dart -subscription.on(LiveQueryEvent.enter, (value) { - print('*** ENTER ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); -}); -``` - -__Leave event__ -When an existing ParseObject’s old value fulfills the QueryBuilder but its new value doesn’t fulfill the QueryBuilder, -you’ll get this event. The object is the ParseObject which leaves the QueryBuilder. -Its content is the latest value of the ParseObject. -```dart -subscription.on(LiveQueryEvent.leave, (value) { - print('*** LEAVE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); -}); -``` - -__Delete event__ -When an existing ParseObject which fulfills the QueryBuilder is deleted, you’ll get this event. -The object is the ParseObject which is deleted -```dart -subscription.on(LiveQueryEvent.delete, (value) { - print('*** DELETE ***: ${DateTime.now().toString()}\n $value '); - print((value as ParseObject).objectId); - print((value as ParseObject).updatedAt); - print((value as ParseObject).createdAt); - print((value as ParseObject).get('objectId')); - print((value as ParseObject).get('updatedAt')); - print((value as ParseObject).get('createdAt')); -}); -``` - -__Unsubscribe__ -If you would like to stop receiving events from a QueryBuilder, you can just unsubscribe the subscription. -After that, you won’t get any events from the subscription object and will close the WebSocket connection to the -LiveQuery server. - -```dart -liveQuery.client.unSubscribe(subscription); -``` - -__Disconnection__ -In case the client's connection to the server breaks, -LiveQuery will automatically try to reconnect. -LiveQuery will wait at increasing intervals between reconnection attempts. -By default, these intervals are set to `[0, 500, 1000, 2000, 5000, 10000]` for mobile and `[0, 500, 1000, 2000, 5000]` for web. -You can change these by providing a custom list using the `liveListRetryIntervals` parameter at `Parse.initialize()` ("-1" means "do not try to reconnect"). - -## ParseLiveList -ParseLiveList makes implementing a dynamic List as simple as possible. - -#### General Use -It ships with the ParseLiveList class itself, this class manages all elements of the list, sorts them, -keeps itself up to date and Notifies you on changes. - -ParseLiveListWidget is a widget that handles all the communication with the ParseLiveList for you. -Using ParseLiveListWidget you can create a dynamic List by just providing a QueryBuilder. - -```dart -ParseLiveListWidget( - query: query, - ); -``` -To customize the List Elements, you can provide a childBuilder. -```dart -ParseLiveListWidget( - query: query, - reverse: false, - childBuilder: - (BuildContext context, ParseLiveListElementSnapshot snapshot) { - if (snapshot.failed) { - return const Text('something went wrong!'); - } else if (snapshot.hasData) { - return ListTile( - title: Text( - snapshot.loadedData.get("text"), - ), - ); - } else { - return const ListTile( - leading: CircularProgressIndicator(), - ); - } - }, -); -``` -Similar to the standard ListView, you can provide arguments like reverse or shrinkWrap. -By providing the listLoadingElement, you can show the user something while the list is loading. -```dart -ParseLiveListWidget( - query: query, - childBuilder: childBuilder, - listLoadingElement: Center( - child: CircularProgressIndicator(), - ), -); -``` -By providing the duration argument, you can change the animation speed. -```dart -ParseLiveListWidget( - query: query, - childBuilder: childBuilder, - duration: Duration(seconds: 1), -); -``` -### included Sub-Objects -By default, ParseLiveQuery will provide you with all the objects you included in your Query like this: -```dart -queryBuilder.includeObject(/*List of all the included sub-objects*/); -``` -ParseLiveList will not listen for updates on this objects by default. -To activate listening for updates on all included objects, add `listenOnAllSubItems: true` to your ParseLiveListWidgets constructor. -If you want ParseLiveList to listen for updates on only some sub-objects, use `listeningIncludes: const [/*all the included sub-objects*/]` instead. -Just as QueryBuilder, ParseLiveList supports nested sub-objects too. - -**NOTE:** To use this features you have to enable [Live Queries](#live-queries) first. - -## Users -You can create and control users just as normal using this SDK. - -To register a user, first create one : -```dart -var user = ParseUser().create("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com"); -``` -Then have the user sign up: - -```dart -var response = await user.signUp(); -if (response.success) user = response.result; -``` -You can also login with the user: -```dart -var response = await user.login(); -if (response.success) user = response.result; -``` -You can also logout with the user: -```dart -var response = await user.logout(); -if (response.success) { - print('User logout'); -} -``` -Also, once logged in you can manage sessions tokens. This feature can be called after Parse().init() on startup to check for a logged in user. -```dart -user = ParseUser.currentUser(); -``` - -To add additional columns to the user: -```dart -var user = ParseUser("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com") - ..set("userLocation", "FlutterLand"); -``` - -Other user features are:- - * Request Password Reset - * Verification Email Request - * Get all users - * Save - * Destroy user - * Queries - - ## Facebook, OAuth and 3rd Party Login/User - - Usually, each provider will provide their own library for logins, but the loginWith method on ParseUser accepts a name of provider, then a Map with the authentication details required. - For Facebook and the example below, we used the library provided at https://pub.dev/packages/flutter_facebook_login - - ``` - Future goToFacebookLogin() async { - final FacebookLogin facebookLogin = FacebookLogin(); - final FacebookLoginResult result = await facebookLogin.logInWithReadPermissions(['email']); - - switch (result.status) { - case FacebookLoginStatus.loggedIn: - final ParseResponse response = await ParseUser.loginWith( - 'facebook', - facebook(result.accessToken.token, - result.accessToken.userId, - result.accessToken.expires)); - - if (response.success) { - // User is logged in, test with ParseUser.currentUser() - } - break; - case FacebookLoginStatus.cancelledByUser: - // User cancelled - break; - case FacebookLoginStatus.error: - // Error - break; - } - } -``` - -For Google and the example below, we used the library provided at https://pub.dev/packages/google_sign_in - -``` -class OAuthLogin { - final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email', 'https://www.googleapis.com/auth/contacts.readonly'] ); - - sigInGoogle() async { - GoogleSignInAccount account = await _googleSignIn.signIn(); - GoogleSignInAuthentication authentication = await account.authentication; - await ParseUser.loginWith( - 'google', - google(_googleSignIn.currentUser.id, - authentication.accessToken, - authentication.idToken)); - } -} -``` - -## Security for Objects - ParseACL -For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object. -To support this type of security, each object has an access control list, implemented by the __ParseACL__ class. - -If ParseACL is not specified (with the exception of the ParseUser class) all objects are set to Public for read and write. -The simplest way to use a ParseACL is to specify that an object may only be read or written by a single user. -To create such an object, there must first be a logged in ParseUser. Then, new ParseACL(user) generates a ParseACL that -limits access to that user. An object’s ACL is updated when the object is saved, like any other property. - -```dart -ParseUser user = await ParseUser.currentUser() as ParseUser; -ParseACL parseACL = ParseACL(owner: user); - -ParseObject parseObject = ParseObject("TestAPI"); -... -parseObject.setACL(parseACL); -var apiResponse = await parseObject.save(); -``` -Permissions can also be granted on a per-user basis. You can add permissions individually to a ParseACL using -__setReadAccess__ and __setWriteAccess__ -```dart -ParseUser user = await ParseUser.currentUser() as ParseUser; -ParseACL parseACL = ParseACL(); -//grant total access to current user -parseACL.setReadAccess(userId: user.objectId, allowed: true); -parseACL.setWriteAccess(userId: user.objectId, allowed: true); -//grant read access to userId: 'TjRuDjuSAO' -parseACL.setReadAccess(userId: 'TjRuDjuSAO', allowed: true); -parseACL.setWriteAccess(userId: 'TjRuDjuSAO', allowed: false); - -ParseObject parseObject = ParseObject("TestAPI"); -... -parseObject.setACL(parseACL); -var apiResponse = await parseObject.save(); -``` -You can also grant permissions to all users at once using setPublicReadAccess and setPublicWriteAccess. -```dart -ParseACL parseACL = ParseACL(); -parseACL.setPublicReadAccess(allowed: true); -parseACL.setPublicWriteAccess(allowed: true); - -ParseObject parseObject = ParseObject("TestAPI"); -... -parseObject.setACL(parseACL); -var apiResponse = await parseObject.save(); -``` -Operations that are forbidden, such as deleting an object that you do not have write access to, result in a -ParseError with code 101: 'ObjectNotFound'. -For security purposes, this prevents clients from distinguishing which object ids exist but are secured, versus which -object ids do not exist at all. - -You can retrieve the ACL list of an object using: -```dart -ParseACL parseACL = parseObject.getACL(); -``` - -## Config -The SDK supports Parse Config. A map of all configs can be grabbed from the server by calling : -```dart -var response = await ParseConfig().getConfigs(); -``` - -and to add a config: -```dart -ParseConfig().addConfig('TestConfig', 'testing'); -``` - -## Cloud Functions -The SDK supports call Cloud Functions. - -Executes a cloud function that returns a ParseObject type -```dart -final ParseCloudFunction function = ParseCloudFunction('hello'); -final ParseResponse result = - await function.executeObjectFunction(); -if (result.success) { - if (result.result is ParseObject) { - final ParseObject parseObject = result.result; - print(parseObject.className); - } -} -``` - -Executes a cloud function with parameters -```dart -final ParseCloudFunction function = ParseCloudFunction('hello'); -final Map params = {'plan': 'paid'}; -function.execute(parameters: params); -``` - -## Relation - -The SDK supports Relation. - -To add relation to object: - -```dart -dietPlan.addRelation('fruits', [ParseObject("Fruits")..set("objectId", "XGadzYxnac")]); -``` - -To remove relation to object: - -```dart -dietPlan.removeRelation('fruits', [ParseObject("Fruits")..set("objectId", "XGadzYxnac")]); -``` - -To Retrive a relation instance for user, call: -```dart -final relation = dietPlan.getRelation('fruits'); -``` - -and then you can add a relation to the passed in object: -``` -relation.add(dietPlan); -final result = await user.save(); -``` - -To retrieve objects that are members of Relation field of a parent object: -```dart -QueryBuilder query = - QueryBuilder(ParseObject('Fruits')) - ..whereRelatedTo('fruits', 'DietPlan', DietPlan.objectId); -``` - -## File -There are three different file classes in this SDK: -- `ParseFileBase` is and abstract class and is the foundation of every file class that can be handled by this SDK. -- `ParseFile` (former the only file class in the SDK) extends ParseFileBase and is by default used as the file class on every platform but web. -This class uses a `File` from `dart:io` for storing the raw file. -- `ParseWebFile` is the equivalent to ParseFile used at Flutter Web. -This class uses an `Uint8List` for storing the raw file. - -These classes are used by default to represent files, but you can also build your own class extending ParseFileBase and provide a custom `ParseFileConstructor` similar to the `SubClasses`. - -Have a look at the example application for a small (non web) example. - - -```dart -//A short example for showing an image from a ParseFileBase -Widget buildImage(ParseFileBase image){ - return FutureBuilder( - future: image.download(), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData) { - if (kIsWeb) { - return Image.memory((snapshot.data as ParseWebFile).file); - } else { - return Image.file((snapshot.data as ParseFile).file); - } - } else { - return CircularProgressIndicator(); - } - }, - ); -} -``` -```dart -//A short example for storing a selected picture -//libraries: image_picker (https://pub.dev/packages/image_picker), image_picker_for_web (https://pub.dev/packages/image_picker_for_web) -PickedFile pickedFile = await ImagePicker().getImage(source: ImageSource.gallery); -ParseFileBase parseFile; -if (kIsWeb) { - //Seems weird, but this lets you get the data from the selected file as an Uint8List very easily. - ParseWebFile file = ParseWebFile(null, name: null, url: pickedFile.path); - await file.download(); - parseFile = ParseWebFile(file.file, name: file.name); -} else { - parseFile = ParseFile(File(pickedFile.path)); -} -someParseObject.set("image", parseFile); -//This saves the ParseObject as well as all of its children, and the ParseFileBase is such a child. -await someParseObject.save(); -``` - -## Other Features of this library -Main: -* Installation (View the example application) -* GeoPoints (View the example application) -* Persistent storage -* Debug Mode - Logging API calls -* Manage Session ID's tokens - -User: -* Queries -* Anonymous (View the example application) -* 3rd Party Authentication - -Objects: -* Create new object -* Extend Parse Object and create local objects that can be saved and retreived -* Queries - -## Author:- +### Author:- This project was authored by Phill Wiggins. You can contact me at phill.wiggins@gmail.com diff --git a/packages/dart/README.md b/packages/dart/README.md new file mode 100644 index 000000000..04ff714f3 --- /dev/null +++ b/packages/dart/README.md @@ -0,0 +1,846 @@ +![Dart Logo](https://dart.dev/assets/shared/dart-logo-for-shares.png?2) ![Parse Logo](https://i2.wp.com/blog.openshift.com/wp-content/uploads/parse-server-logo-1.png?fit=200%2C200&ssl=1&resize=350%2C200) + + +**THIS README IS WORK IN PROGRESS** + +## Parse For Dart! +This is a Dart package that allows communication with a Parse Server, (https://parseplatform.org) either hosted on your own server or another, like (http://Back4App.com). + +This is a work in progress and we are consistently updating it. Please let us know if you think anything needs changing/adding, and more than ever, please do join in on this project. (Even if it is just to improve our documentation) + +## Join in! +Want to get involved? Join our Slack channel and help out! (http://flutter-parse-sdk.slack.com) + +## Getting Started +To install, either add to your pubspec.yaml +```yml +dependencies: + parse_server_sdk: ^1.0.28 +``` +or clone this repository and add to your project. As this is an early development with multiple contributors, it is probably best to download/clone and keep updating as an when a new feature is added. + + +Once you have the library added to your project, upon first call to your app (Similar to what your application class would be) add the following... + +```dart +await Parse().initialize( + keyApplicationId, + keyParseServerUrl, + ); +``` + +If you want to use secure storage or use the Flutter web/desktop SDK, please change to the below instance of CoreStorage as it has no dependencies on Flutter. + +**The `CoreStoreSembastImp` does not encrypt the data on web!** (Web is not safe anyway. Encrypt fields manually as needed.) +```dart + +await Parse().initialize( + keyParseApplicationId, + keyParseServerUrl, + coreStore: await CoreStoreSembastImp.getInstance()); +``` +It's possible to add other parameters to work with your instance of Parse Server:- + +```dart + await Parse().initialize( + keyApplicationId, + keyParseServerUrl, + masterKey: keyParseMasterKey, // Required for Back4App and others + clientKey: keyParseClientKey, // Required for some setups + debug: true, // When enabled, prints logs to console + liveQueryUrl: keyLiveQueryUrl, // Required if using LiveQuery + autoSendSessionId: true, // Required for authentication and ACL + securityContext: securityContext, // Again, required for some setups + coreStore: await CoreStoreSharedPrefsImp.getInstance()); // Local data storage method. Will use SharedPreferences instead of Sembast as an internal DB +``` + + +#### Early Web support +Currently this requires adding `X-Parse-Installation-Id` as an allowed header to parse-server. +When running directly via docker, set the env var `PARSE_SERVER_ALLOW_HEADERS=X-Parse-Installation-Id`. +When running via express, set [ParseServerOptions](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `allowHeaders: ['X-Parse-Installation-Id']`. + +Be aware that for web ParseInstallation does include app name, version or package identifier. + + +## Objects +You can create custom objects by calling: +```dart +var dietPlan = ParseObject('DietPlan') + ..set('Name', 'Ketogenic') + ..set('Fat', 65); +await dietPlan.save(); +``` +Or update existing object by its objectId by calling: +```dart +var dietPlan = ParseObject('DietPlan') + ..objectId = 'R5EonpUDWy' + ..set('Fat', 70); +await dietPlan.save(); +``` +Verify that the object has been successfully saved using +```dart +var response = await dietPlan.save(); +if (response.success) { + dietPlan = response.results.first; +} +``` +Types supported: + * String + * Double + * Int + * Boolean + * DateTime + * File + * Geopoint + * ParseObject/ParseUser (Pointer) + * Map + * List (all types supported) + +You then have the ability to do the following with that object: +The features available are:- + * Get + * GetAll + * Create + * Save + * Query - By object Id + * Delete + * Complex queries as shown above + * Pin + * Plenty more + * Counters + * Array Operators + +## Custom Objects +You can create your own `ParseObjects` or convert your existing objects into Parse Objects by doing the following: + +```dart +class DietPlan extends ParseObject implements ParseCloneable { + + DietPlan() : super(_keyTableName); + DietPlan.clone(): this(); + + /// Looks strangely hacky but due to Flutter not using reflection, we have to + /// mimic a clone + @override clone(Map map) => DietPlan.clone()..fromJson(map); + + static const String _keyTableName = 'Diet_Plans'; + static const String keyName = 'Name'; + + String get name => get(keyName); + set name(String name) => set(keyName, name); +} + +``` + +When receiving an `ParseObject` from the SDK, you can often provide an instance of your custom object as an copy object. +To always use your custom object class, you can register your subclass at the initialization of the SDK. +```dart +Parse().initialize( + ..., + registeredSubClassMap: { + 'Diet_Plans': () => DietPlan(), + }, + parseUserConstructor: (username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress), +); +``` +Additionally you can register `SubClasses` after the initialization of the SDK. +```dart +ParseCoreData().registerSubClass('Diet_Plans', () => DietPlan()); +ParseCoreData().registerUserSubClass((username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress)); +``` +Providing a `ParseObject` as described above should still work, even if you have registered a different `SubClass`. + +For custom file classes have a lock at [here](#File). + +## Add new values to objects +To add a variable to an object call and retrieve it, call + +```dart +dietPlan.set('RandomInt', 8); +var randomInt = dietPlan.get('RandomInt'); +``` + +## Save objects using pins +You can now save an object by calling `.pin()` on an instance of an object + +```dart +dietPlan.pin(); +``` + +and to retrieve it + +```dart +var dietPlan = DietPlan().fromPin('OBJECT ID OF OBJECT'); +``` + +## Storage +We now have 2 types of storage, secure and unsecure. We currently rely on 2 third party options: + +- SharedPreferences +- Sembast +Sembast offers secured storage, whilst SharePreferences wraps NSUserDefaults (on iOS) and SharedPreferences (on Android). + +The storage method is defined in the parameter __coreStore__ in Parse().initialize + +Check sample code for options + +## Increment Counter values in objects +Retrieve it, call + +```dart +var response = await dietPlan.increment("count", 1); + +``` +or using with save function + +```dart +dietPlan.setIncrement('count', 1); +dietPlan.setDecrement('count', 1); +var response = dietPlan.save() + +``` + +## Array Operator in objects +Retrieve it, call + +```dart +var response = await dietPlan.add("listKeywords", ["a", "a","d"]); + +var response = await dietPlan.addUnique("listKeywords", ["a", "a","d"]); + +var response = await dietPlan.remove("listKeywords", ["a"]); + +``` +or using with save function + +```dart +dietPlan.setAdd('listKeywords', ['a','a','d']); +dietPlan.setAddUnique('listKeywords', ['a','a','d']); +dietPlan.setRemove('listKeywords', ['a']); +var response = dietPlan.save() +``` + +## Queries +Once you have setup the project and initialised the instance, you can then retreive data from your server by calling: +```dart +var apiResponse = await ParseObject('ParseTableName').getAll(); + +if (apiResponse.success){ + for (var testObject in apiResponse.result) { + print(ApplicationConstants.APP_NAME + ": " + testObject.toString()); + } +} +``` +Or you can get an object by its objectId: + +```dart +var dietPlan = await DietPlan().getObject('R5EonpUDWy'); + +if (dietPlan.success) { + print(ApplicationConstants.keyAppName + ": " + (dietPlan.result as DietPlan).toString()); +} else { + print(ApplicationConstants.keyAppName + ": " + dietPlan.exception.message); +} +``` + +## Complex queries +You can create complex queries to really put your database to the test: + +```dart +var queryBuilder = QueryBuilder(DietPlan()) + ..startsWith(DietPlan.keyName, "Keto") + ..greaterThan(DietPlan.keyFat, 64) + ..lessThan(DietPlan.keyFat, 66) + ..equals(DietPlan.keyCarbs, 5); + +var response = await queryBuilder.query(); + +if (response.success) { + print(ApplicationConstants.keyAppName + ": " + ((response.results as List).first as DietPlan).toString()); +} else { + print(ApplicationConstants.keyAppName + ": " + response.exception.message); +} +``` + +if you want to find objects that match one of several queries, you can use __QueryBuilder.or__ method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do: +```dart +ParseObject playerObject = ParseObject("Player"); + +QueryBuilder lotsOfWins = + QueryBuilder(playerObject)) + ..whereGreaterThan('wins', 50); + +QueryBuilder fewWins = + QueryBuilder(playerObject) + ..whereLessThan('wins', 5); + +QueryBuilder mainQuery = QueryBuilder.or( + playerObject, + [lotsOfWins, fewWins], + ); + +var apiResponse = await mainQuery.query(); +``` + +The features available are:- + * Equals + * Contains + * LessThan + * LessThanOrEqualTo + * GreaterThan + * GreaterThanOrEqualTo + * NotEqualTo + * StartsWith + * EndsWith + * Exists + * Near + * WithinMiles + * WithinKilometers + * WithinRadians + * WithinGeoBox + * MatchesQuery + * DoesNotMatchQuery + * MatchesKeyInQuery + * DoesNotMatchKeyInQuery + * Regex + * Order + * Limit + * Skip + * Ascending + * Descending + * Plenty more! + +## Relational queries +If you want to retrieve objects where a field contains an object that matches another query, you can use the +__whereMatchesQuery__ condition. +For example, imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. +You can find comments on posts with images by doing: + +```dart +QueryBuilder queryPost = + QueryBuilder(ParseObject('Post')) + ..whereValueExists('image', true); + +QueryBuilder queryComment = + QueryBuilder(ParseObject('Comment')) + ..whereMatchesQuery('post', queryPost); + +var apiResponse = await queryComment.query(); +``` + +If you want to retrieve objects where a field contains an object that does not match another query, you can use the +__whereDoesNotMatchQuery__ condition. +Imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. +You can find comments on posts without images by doing: + +```dart +QueryBuilder queryPost = + QueryBuilder(ParseObject('Post')) + ..whereValueExists('image', true); + +QueryBuilder queryComment = + QueryBuilder(ParseObject('Comment')) + ..whereDoesNotMatchQuery('post', queryPost); + +var apiResponse = await queryComment.query(); +``` + +You can use the __whereMatchesKeyInQuery__ method to get objects where a key matches the value of a key in a set of objects resulting from another query. For example, if you have a class containing sports teams and you store a user’s hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like:: + +```dart +QueryBuilder teamQuery = + QueryBuilder(ParseObject('Team')) + ..whereGreaterThan('winPct', 0.5); + +QueryBuilder userQuery = + QueryBuilderParseUser.forQuery()) + ..whereMatchesKeyInQuery('hometown', 'city', teamQuery); + +var apiResponse = await userQuery.query(); +``` + +Conversely, to get objects where a key does not match the value of a key in a set of objects resulting from another query, use __whereDoesNotMatchKeyInQuery__. For example, to find users whose hometown teams have losing records: + +```dart +QueryBuilder teamQuery = + QueryBuilder(ParseObject('Team')) + ..whereGreaterThan('winPct', 0.5); + +QueryBuilder losingUserQuery = + QueryBuilderParseUser.forQuery()) + ..whereDoesNotMatchKeyInQuery('hometown', 'city', teamQuery); + +var apiResponse = await losingUserQuery.query(); +``` + +To filter rows based on objectId’s from pointers in a second table, you can use dot notation: +```dart +QueryBuilder rolesOfTypeX = + QueryBuilder(ParseObject('Role')) + ..whereEqualTo('type', 'x'); + +QueryBuilder groupsWithRoleX = + QueryBuilder(ParseObject('Group'))) + ..whereMatchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); + +var apiResponse = await groupsWithRoleX.query(); +``` + +## Counting Objects +If you only care about the number of games played by a particular player: + +```dart +QueryBuilder queryPlayers = + QueryBuilder(ParseObject('GameScore')) + ..whereEqualTo('playerName', 'Jonathan Walsh'); +var apiResponse = await queryPlayers.count(); +if (apiResponse.success && apiResponse.result != null) { + int countGames = apiResponse.count; +} +``` + +## Live Queries +This tool allows you to subscribe to a QueryBuilder you are interested in. Once subscribed, the server will notify clients +whenever a ParseObject that matches the QueryBuilder is created or updated, in real-time. + +Parse LiveQuery contains two parts, the LiveQuery server and the LiveQuery clients. In order to use live queries, you need +to set up both of them. + +The Parse Server configuration guide on the server is found here https://docs.parseplatform.org/parse-server/guide/#live-queries and is not part of this documentation. + +Initialize the Parse Live Query by entering the parameter liveQueryUrl in Parse().initialize: +```dart +Parse().initialize( + keyApplicationId, + keyParseServerUrl, + clientKey: keyParseClientKey, + debug: true, + liveQueryUrl: keyLiveQueryUrl, + autoSendSessionId: true); +``` + +Declare LiveQuery: +```dart +final LiveQuery liveQuery = LiveQuery(); +``` + +Set the QueryBuilder that will be monitored by LiveQuery: +```dart +QueryBuilder query = + QueryBuilder(ParseObject('TestAPI')) + ..whereEqualTo('intNumber', 1); +``` +__Create a subscription__ +You’ll get the LiveQuery events through this subscription. +The first time you call subscribe, we’ll try to open the WebSocket connection to the LiveQuery server for you. + +```dart +Subscription subscription = await liveQuery.client.subscribe(query); +``` + +__Event Handling__ +We define several types of events you’ll get through a subscription object: + +__Create event__ +When a new ParseObject is created and it fulfills the QueryBuilder you subscribe, you’ll get this event. +The object is the ParseObject which was created. +```dart +subscription.on(LiveQueryEvent.create, (value) { + print('*** CREATE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Update event__ +When an existing ParseObject which fulfills the QueryBuilder you subscribe is updated (The ParseObject fulfills the +QueryBuilder before and after changes), you’ll get this event. +The object is the ParseObject which was updated. Its content is the latest value of the ParseObject. +```dart +subscription.on(LiveQueryEvent.update, (value) { + print('*** UPDATE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Enter event__ +When an existing ParseObject’s old value does not fulfill the QueryBuilder but its new value fulfills the QueryBuilder, +you’ll get this event. The object is the ParseObject which enters the QueryBuilder. +Its content is the latest value of the ParseObject. +```dart +subscription.on(LiveQueryEvent.enter, (value) { + print('*** ENTER ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Leave event__ +When an existing ParseObject’s old value fulfills the QueryBuilder but its new value doesn’t fulfill the QueryBuilder, +you’ll get this event. The object is the ParseObject which leaves the QueryBuilder. +Its content is the latest value of the ParseObject. +```dart +subscription.on(LiveQueryEvent.leave, (value) { + print('*** LEAVE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Delete event__ +When an existing ParseObject which fulfills the QueryBuilder is deleted, you’ll get this event. +The object is the ParseObject which is deleted +```dart +subscription.on(LiveQueryEvent.delete, (value) { + print('*** DELETE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Unsubscribe__ +If you would like to stop receiving events from a QueryBuilder, you can just unsubscribe the subscription. +After that, you won’t get any events from the subscription object and will close the WebSocket connection to the +LiveQuery server. + +```dart +liveQuery.client.unSubscribe(subscription); +``` + +__Disconnection__ +In case the client's connection to the server breaks, +LiveQuery will automatically try to reconnect. +LiveQuery will wait at increasing intervals between reconnection attempts. +By default, these intervals are set to `[0, 500, 1000, 2000, 5000, 10000]` for mobile and `[0, 500, 1000, 2000, 5000]` for web. +You can change these by providing a custom list using the `liveListRetryIntervals` parameter at `Parse.initialize()` ("-1" means "do not try to reconnect"). + +## ParseLiveList +ParseLiveList makes implementing a dynamic List as simple as possible. + +### General Use +It ships with the ParseLiveList class itself, this class manages all elements of the list, sorts them, +keeps itself up to date and Notifies you on changes. + +### included Sub-Objects +By default, ParseLiveQuery will provide you with all the objects you included in your Query like this: +```dart +queryBuilder.includeObject(/*List of all the included sub-objects*/); +``` +ParseLiveList will not listen for updates on this objects by default. +To activate listening for updates on all included objects, add `listenOnAllSubItems: true` to your ParseLiveListWidgets constructor. +If you want ParseLiveList to listen for updates on only some sub-objects, use `listeningIncludes: const [/*all the included sub-objects*/]` instead. +Just as QueryBuilder, ParseLiveList supports nested sub-objects too. + +**NOTE:** To use this features you have to enable [Live Queries](#live-queries) first. + +## Users +You can create and control users just as normal using this SDK. + +To register a user, first create one : +```dart +var user = ParseUser().create("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com"); +``` +Then have the user sign up: + +```dart +var response = await user.signUp(); +if (response.success) user = response.result; +``` +You can also login with the user: +```dart +var response = await user.login(); +if (response.success) user = response.result; +``` +You can also logout with the user: +```dart +var response = await user.logout(); +if (response.success) { + print('User logout'); +} +``` +Also, once logged in you can manage sessions tokens. This feature can be called after Parse().init() on startup to check for a logged in user. +```dart +user = ParseUser.currentUser(); +``` + +To add additional columns to the user: +```dart +var user = ParseUser("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com") + ..set("userLocation", "FlutterLand"); +``` + +Other user features are:- + * Request Password Reset + * Verification Email Request + * Get all users + * Save + * Destroy user + * Queries + + ## Facebook, OAuth and 3rd Party Login/User + + Usually, each provider will provide their own library for logins, but the loginWith method on ParseUser accepts a name of provider, then a Map with the authentication details required. + For Facebook and the example below, we used the library provided at https://pub.dev/packages/flutter_facebook_login + + ``` + Future goToFacebookLogin() async { + final FacebookLogin facebookLogin = FacebookLogin(); + final FacebookLoginResult result = await facebookLogin.logInWithReadPermissions(['email']); + + switch (result.status) { + case FacebookLoginStatus.loggedIn: + final ParseResponse response = await ParseUser.loginWith( + 'facebook', + facebook(result.accessToken.token, + result.accessToken.userId, + result.accessToken.expires)); + + if (response.success) { + // User is logged in, test with ParseUser.currentUser() + } + break; + case FacebookLoginStatus.cancelledByUser: + // User cancelled + break; + case FacebookLoginStatus.error: + // Error + break; + } + } +``` + +For Google and the example below, we used the library provided at https://pub.dev/packages/google_sign_in + +``` +class OAuthLogin { + final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email', 'https://www.googleapis.com/auth/contacts.readonly'] ); + + sigInGoogle() async { + GoogleSignInAccount account = await _googleSignIn.signIn(); + GoogleSignInAuthentication authentication = await account.authentication; + await ParseUser.loginWith( + 'google', + google(_googleSignIn.currentUser.id, + authentication.accessToken, + authentication.idToken)); + } +} +``` + +## Security for Objects - ParseACL +For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object. +To support this type of security, each object has an access control list, implemented by the __ParseACL__ class. + +If ParseACL is not specified (with the exception of the ParseUser class) all objects are set to Public for read and write. +The simplest way to use a ParseACL is to specify that an object may only be read or written by a single user. +To create such an object, there must first be a logged in ParseUser. Then, new ParseACL(user) generates a ParseACL that +limits access to that user. An object’s ACL is updated when the object is saved, like any other property. + +```dart +ParseUser user = await ParseUser.currentUser() as ParseUser; +ParseACL parseACL = ParseACL(owner: user); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +Permissions can also be granted on a per-user basis. You can add permissions individually to a ParseACL using +__setReadAccess__ and __setWriteAccess__ +```dart +ParseUser user = await ParseUser.currentUser() as ParseUser; +ParseACL parseACL = ParseACL(); +//grant total access to current user +parseACL.setReadAccess(userId: user.objectId, allowed: true); +parseACL.setWriteAccess(userId: user.objectId, allowed: true); +//grant read access to userId: 'TjRuDjuSAO' +parseACL.setReadAccess(userId: 'TjRuDjuSAO', allowed: true); +parseACL.setWriteAccess(userId: 'TjRuDjuSAO', allowed: false); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +You can also grant permissions to all users at once using setPublicReadAccess and setPublicWriteAccess. +```dart +ParseACL parseACL = ParseACL(); +parseACL.setPublicReadAccess(allowed: true); +parseACL.setPublicWriteAccess(allowed: true); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +Operations that are forbidden, such as deleting an object that you do not have write access to, result in a +ParseError with code 101: 'ObjectNotFound'. +For security purposes, this prevents clients from distinguishing which object ids exist but are secured, versus which +object ids do not exist at all. + +You can retrieve the ACL list of an object using: +```dart +ParseACL parseACL = parseObject.getACL(); +``` + +## Config +The SDK supports Parse Config. A map of all configs can be grabbed from the server by calling : +```dart +var response = await ParseConfig().getConfigs(); +``` + +and to add a config: +```dart +ParseConfig().addConfig('TestConfig', 'testing'); +``` + +## Cloud Functions +The SDK supports call Cloud Functions. + +Executes a cloud function that returns a ParseObject type +```dart +final ParseCloudFunction function = ParseCloudFunction('hello'); +final ParseResponse result = + await function.executeObjectFunction(); +if (result.success) { + if (result.result is ParseObject) { + final ParseObject parseObject = result.result; + print(parseObject.className); + } +} +``` + +Executes a cloud function with parameters +```dart +final ParseCloudFunction function = ParseCloudFunction('hello'); +final Map params = {'plan': 'paid'}; +function.execute(parameters: params); +``` + +## Relation + +The SDK supports Relation. + +To add relation to object: + +```dart +dietPlan.addRelation('fruits', [ParseObject("Fruits")..set("objectId", "XGadzYxnac")]); +``` + +To remove relation to object: + +```dart +dietPlan.removeRelation('fruits', [ParseObject("Fruits")..set("objectId", "XGadzYxnac")]); +``` + +To Retrive a relation instance for user, call: +```dart +final relation = dietPlan.getRelation('fruits'); +``` + +and then you can add a relation to the passed in object: +``` +relation.add(dietPlan); +final result = await user.save(); +``` + +To retrieve objects that are members of Relation field of a parent object: +```dart +QueryBuilder query = + QueryBuilder(ParseObject('Fruits')) + ..whereRelatedTo('fruits', 'DietPlan', DietPlan.objectId); +``` + +## File +There are three different file classes in this SDK: +- `ParseFileBase` is and abstract class and is the foundation of every file class that can be handled by this SDK. +- `ParseFile` (former the only file class in the SDK) extends ParseFileBase and is by default used as the file class on every platform but web. +This class uses a `File` from `dart:io` for storing the raw file. +- `ParseWebFile` is the equivalent to ParseFile used at Flutter Web. +This class uses an `Uint8List` for storing the raw file. + +These classes are used by default to represent files, but you can also build your own class extending ParseFileBase and provide a custom `ParseFileConstructor` similar to the `SubClasses`. + +Have a look at the example application for a small (non web) example. + + +```dart +//A short example for showing an image from a ParseFileBase +Widget buildImage(ParseFileBase image){ + return FutureBuilder( + future: image.download(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData) { + if (kIsWeb) { + return Image.memory((snapshot.data as ParseWebFile).file); + } else { + return Image.file((snapshot.data as ParseFile).file); + } + } else { + return CircularProgressIndicator(); + } + }, + ); +} +``` +```dart +//A short example for storing a selected picture +//libraries: image_picker (https://pub.dev/packages/image_picker), image_picker_for_web (https://pub.dev/packages/image_picker_for_web) +PickedFile pickedFile = await ImagePicker().getImage(source: ImageSource.gallery); +ParseFileBase parseFile; +if (kIsWeb) { + //Seems weird, but this lets you get the data from the selected file as an Uint8List very easily. + ParseWebFile file = ParseWebFile(null, name: null, url: pickedFile.path); + await file.download(); + parseFile = ParseWebFile(file.file, name: file.name); +} else { + parseFile = ParseFile(File(pickedFile.path)); +} +someParseObject.set("image", parseFile); +//This saves the ParseObject as well as all of its children, and the ParseFileBase is such a child. +await someParseObject.save(); +``` + +## Other Features of this library +Main: +* Installation (View the example application) +* GeoPoints (View the example application) +* Persistent storage +* Debug Mode - Logging API calls +* Manage Session ID's tokens + +User: +* Queries +* Anonymous (View the example application) +* 3rd Party Authentication + +Objects: +* Create new object +* Extend Parse Object and create local objects that can be saved and retreived +* Queries + +## Author:- +This project was authored by Phill Wiggins. You can contact me at phill.wiggins@gmail.com diff --git a/lib/parse_server_sdk_dart.dart b/packages/dart/lib/parse_server_sdk.dart similarity index 100% rename from lib/parse_server_sdk_dart.dart rename to packages/dart/lib/parse_server_sdk.dart diff --git a/lib/src/base/parse_constants.dart b/packages/dart/lib/src/base/parse_constants.dart similarity index 100% rename from lib/src/base/parse_constants.dart rename to packages/dart/lib/src/base/parse_constants.dart diff --git a/lib/src/data/app_info.dart b/packages/dart/lib/src/data/app_info.dart similarity index 100% rename from lib/src/data/app_info.dart rename to packages/dart/lib/src/data/app_info.dart diff --git a/lib/src/data/core_store.dart b/packages/dart/lib/src/data/core_store.dart similarity index 100% rename from lib/src/data/core_store.dart rename to packages/dart/lib/src/data/core_store.dart diff --git a/lib/src/data/parse_core_data.dart b/packages/dart/lib/src/data/parse_core_data.dart similarity index 100% rename from lib/src/data/parse_core_data.dart rename to packages/dart/lib/src/data/parse_core_data.dart diff --git a/lib/src/data/parse_subclass_handler.dart b/packages/dart/lib/src/data/parse_subclass_handler.dart similarity index 100% rename from lib/src/data/parse_subclass_handler.dart rename to packages/dart/lib/src/data/parse_subclass_handler.dart diff --git a/lib/src/enums/parse_enum_api_rq.dart b/packages/dart/lib/src/enums/parse_enum_api_rq.dart similarity index 100% rename from lib/src/enums/parse_enum_api_rq.dart rename to packages/dart/lib/src/enums/parse_enum_api_rq.dart diff --git a/lib/src/network/parse_connectivity.dart b/packages/dart/lib/src/network/parse_connectivity.dart similarity index 100% rename from lib/src/network/parse_connectivity.dart rename to packages/dart/lib/src/network/parse_connectivity.dart diff --git a/lib/src/network/parse_http_client.dart b/packages/dart/lib/src/network/parse_http_client.dart similarity index 100% rename from lib/src/network/parse_http_client.dart rename to packages/dart/lib/src/network/parse_http_client.dart diff --git a/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart similarity index 100% rename from lib/src/network/parse_live_query.dart rename to packages/dart/lib/src/network/parse_live_query.dart diff --git a/lib/src/network/parse_query.dart b/packages/dart/lib/src/network/parse_query.dart similarity index 100% rename from lib/src/network/parse_query.dart rename to packages/dart/lib/src/network/parse_query.dart diff --git a/lib/src/network/parse_websocket.dart b/packages/dart/lib/src/network/parse_websocket.dart similarity index 100% rename from lib/src/network/parse_websocket.dart rename to packages/dart/lib/src/network/parse_websocket.dart diff --git a/lib/src/network/parse_websocket_html.dart b/packages/dart/lib/src/network/parse_websocket_html.dart similarity index 100% rename from lib/src/network/parse_websocket_html.dart rename to packages/dart/lib/src/network/parse_websocket_html.dart diff --git a/lib/src/network/parse_websocket_io.dart b/packages/dart/lib/src/network/parse_websocket_io.dart similarity index 100% rename from lib/src/network/parse_websocket_io.dart rename to packages/dart/lib/src/network/parse_websocket_io.dart diff --git a/lib/src/objects/parse_acl.dart b/packages/dart/lib/src/objects/parse_acl.dart similarity index 100% rename from lib/src/objects/parse_acl.dart rename to packages/dart/lib/src/objects/parse_acl.dart diff --git a/lib/src/objects/parse_base.dart b/packages/dart/lib/src/objects/parse_base.dart similarity index 100% rename from lib/src/objects/parse_base.dart rename to packages/dart/lib/src/objects/parse_base.dart diff --git a/lib/src/objects/parse_cloneable.dart b/packages/dart/lib/src/objects/parse_cloneable.dart similarity index 100% rename from lib/src/objects/parse_cloneable.dart rename to packages/dart/lib/src/objects/parse_cloneable.dart diff --git a/lib/src/objects/parse_config.dart b/packages/dart/lib/src/objects/parse_config.dart similarity index 100% rename from lib/src/objects/parse_config.dart rename to packages/dart/lib/src/objects/parse_config.dart diff --git a/lib/src/objects/parse_error.dart b/packages/dart/lib/src/objects/parse_error.dart similarity index 100% rename from lib/src/objects/parse_error.dart rename to packages/dart/lib/src/objects/parse_error.dart diff --git a/lib/src/objects/parse_file.dart b/packages/dart/lib/src/objects/parse_file.dart similarity index 100% rename from lib/src/objects/parse_file.dart rename to packages/dart/lib/src/objects/parse_file.dart diff --git a/lib/src/objects/parse_file_base.dart b/packages/dart/lib/src/objects/parse_file_base.dart similarity index 100% rename from lib/src/objects/parse_file_base.dart rename to packages/dart/lib/src/objects/parse_file_base.dart diff --git a/lib/src/objects/parse_file_web.dart b/packages/dart/lib/src/objects/parse_file_web.dart similarity index 100% rename from lib/src/objects/parse_file_web.dart rename to packages/dart/lib/src/objects/parse_file_web.dart diff --git a/lib/src/objects/parse_function.dart b/packages/dart/lib/src/objects/parse_function.dart similarity index 100% rename from lib/src/objects/parse_function.dart rename to packages/dart/lib/src/objects/parse_function.dart diff --git a/lib/src/objects/parse_geo_point.dart b/packages/dart/lib/src/objects/parse_geo_point.dart similarity index 100% rename from lib/src/objects/parse_geo_point.dart rename to packages/dart/lib/src/objects/parse_geo_point.dart diff --git a/lib/src/objects/parse_installation.dart b/packages/dart/lib/src/objects/parse_installation.dart similarity index 100% rename from lib/src/objects/parse_installation.dart rename to packages/dart/lib/src/objects/parse_installation.dart diff --git a/lib/src/objects/parse_merge.dart b/packages/dart/lib/src/objects/parse_merge.dart similarity index 100% rename from lib/src/objects/parse_merge.dart rename to packages/dart/lib/src/objects/parse_merge.dart diff --git a/lib/src/objects/parse_object.dart b/packages/dart/lib/src/objects/parse_object.dart similarity index 100% rename from lib/src/objects/parse_object.dart rename to packages/dart/lib/src/objects/parse_object.dart diff --git a/lib/src/objects/parse_relation.dart b/packages/dart/lib/src/objects/parse_relation.dart similarity index 100% rename from lib/src/objects/parse_relation.dart rename to packages/dart/lib/src/objects/parse_relation.dart diff --git a/lib/src/objects/parse_response.dart b/packages/dart/lib/src/objects/parse_response.dart similarity index 100% rename from lib/src/objects/parse_response.dart rename to packages/dart/lib/src/objects/parse_response.dart diff --git a/lib/src/objects/parse_session.dart b/packages/dart/lib/src/objects/parse_session.dart similarity index 100% rename from lib/src/objects/parse_session.dart rename to packages/dart/lib/src/objects/parse_session.dart diff --git a/lib/src/objects/parse_user.dart b/packages/dart/lib/src/objects/parse_user.dart similarity index 100% rename from lib/src/objects/parse_user.dart rename to packages/dart/lib/src/objects/parse_user.dart diff --git a/lib/src/objects/response/parse_error_response.dart b/packages/dart/lib/src/objects/response/parse_error_response.dart similarity index 100% rename from lib/src/objects/response/parse_error_response.dart rename to packages/dart/lib/src/objects/response/parse_error_response.dart diff --git a/lib/src/objects/response/parse_exception_response.dart b/packages/dart/lib/src/objects/response/parse_exception_response.dart similarity index 100% rename from lib/src/objects/response/parse_exception_response.dart rename to packages/dart/lib/src/objects/response/parse_exception_response.dart diff --git a/lib/src/objects/response/parse_response_builder.dart b/packages/dart/lib/src/objects/response/parse_response_builder.dart similarity index 100% rename from lib/src/objects/response/parse_response_builder.dart rename to packages/dart/lib/src/objects/response/parse_response_builder.dart diff --git a/lib/src/objects/response/parse_response_utils.dart b/packages/dart/lib/src/objects/response/parse_response_utils.dart similarity index 100% rename from lib/src/objects/response/parse_response_utils.dart rename to packages/dart/lib/src/objects/response/parse_response_utils.dart diff --git a/lib/src/objects/response/parse_success_no_results.dart b/packages/dart/lib/src/objects/response/parse_success_no_results.dart similarity index 100% rename from lib/src/objects/response/parse_success_no_results.dart rename to packages/dart/lib/src/objects/response/parse_success_no_results.dart diff --git a/lib/src/storage/core_store.dart b/packages/dart/lib/src/storage/core_store.dart similarity index 100% rename from lib/src/storage/core_store.dart rename to packages/dart/lib/src/storage/core_store.dart diff --git a/lib/src/storage/core_store_sem_impl.dart b/packages/dart/lib/src/storage/core_store_sem_impl.dart similarity index 100% rename from lib/src/storage/core_store_sem_impl.dart rename to packages/dart/lib/src/storage/core_store_sem_impl.dart diff --git a/lib/src/storage/xxtea_codec.dart b/packages/dart/lib/src/storage/xxtea_codec.dart similarity index 100% rename from lib/src/storage/xxtea_codec.dart rename to packages/dart/lib/src/storage/xxtea_codec.dart diff --git a/lib/src/utils/parse_date_format.dart b/packages/dart/lib/src/utils/parse_date_format.dart similarity index 100% rename from lib/src/utils/parse_date_format.dart rename to packages/dart/lib/src/utils/parse_date_format.dart diff --git a/lib/src/utils/parse_decoder.dart b/packages/dart/lib/src/utils/parse_decoder.dart similarity index 100% rename from lib/src/utils/parse_decoder.dart rename to packages/dart/lib/src/utils/parse_decoder.dart diff --git a/lib/src/utils/parse_encoder.dart b/packages/dart/lib/src/utils/parse_encoder.dart similarity index 100% rename from lib/src/utils/parse_encoder.dart rename to packages/dart/lib/src/utils/parse_encoder.dart diff --git a/lib/src/utils/parse_file_extensions.dart b/packages/dart/lib/src/utils/parse_file_extensions.dart similarity index 100% rename from lib/src/utils/parse_file_extensions.dart rename to packages/dart/lib/src/utils/parse_file_extensions.dart diff --git a/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart similarity index 100% rename from lib/src/utils/parse_live_list.dart rename to packages/dart/lib/src/utils/parse_live_list.dart diff --git a/lib/src/utils/parse_logger.dart b/packages/dart/lib/src/utils/parse_logger.dart similarity index 100% rename from lib/src/utils/parse_logger.dart rename to packages/dart/lib/src/utils/parse_logger.dart diff --git a/lib/src/utils/parse_login_helpers.dart b/packages/dart/lib/src/utils/parse_login_helpers.dart similarity index 100% rename from lib/src/utils/parse_login_helpers.dart rename to packages/dart/lib/src/utils/parse_login_helpers.dart diff --git a/lib/src/utils/parse_utils.dart b/packages/dart/lib/src/utils/parse_utils.dart similarity index 100% rename from lib/src/utils/parse_utils.dart rename to packages/dart/lib/src/utils/parse_utils.dart diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml new file mode 100644 index 000000000..888de316e --- /dev/null +++ b/packages/dart/pubspec.yaml @@ -0,0 +1,29 @@ +name: parse_server_sdk +description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) +version: 1.0.28 +homepage: https://github.com/phillwiggins/flutter_parse_sdk + +environment: + sdk: ">=2.2.2 <3.0.0" + +dependencies: + http: ^0.12.2 + + # Networking + web_socket_channel: ^1.1.0 + + #Database + sembast: ^2.4.7+6 + sembast_web: '>=1.0.0' + xxtea: ^2.0.3 + + # Utils + uuid: ^2.2.2 + meta: ^1.1.8 + path: ^1.7.0 + +dev_dependencies: + # Testing + flutter_test: + sdk: flutter + mockito: ^4.1.1 diff --git a/packages/flutter/README.md b/packages/flutter/README.md new file mode 100644 index 000000000..800477861 --- /dev/null +++ b/packages/flutter/README.md @@ -0,0 +1,893 @@ + +![Parse Logo](https://upload.wikimedia.org/wikipedia/commons/1/17/Google-flutter-logo.png)![Flutter Logo](https://i2.wp.com/blog.openshift.com/wp-content/uploads/parse-server-logo-1.png?fit=200%2C200&ssl=1&resize=350%2C200) + +## Parse For Flutter! +Hi, this is a Flutter plugin that allows communication with a Parse Server, (https://parseplatform.org) either hosted on your own server or another, like (http://Back4App.com). + +This is a work in progress and we are consistently updating it. Please let us know if you think anything needs changing/adding, and more than ever, please do join in on this project. (Even if it is just to improve our documentation) + +## Join in! +Want to get involved? Join our Slack channel and help out! (http://flutter-parse-sdk.slack.com) + +## Getting Started +To install, either add to your pubspec.yaml +```yml +dependencies: + parse_server_sdk: ^1.0.28 +``` +or clone this repository and add to your project. As this is an early development with multiple contributors, it is probably best to download/clone and keep updating as an when a new feature is added. + + +Once you have the library added to your project, upon first call to your app (Similar to what your application class would be) add the following... + +```dart +await Parse().initialize( + keyApplicationId, + keyParseServerUrl); +``` + +If you want to use secure storage or use the Flutter web/desktop SDK, please change to the below instance of CoreStorage as it has no dependencies on Flutter. + +**The `CoreStoreSembastImp` does not encrypt the data on web!** (Web is not safe anyway. Encrypt fields manually as needed.) +```dart + +await Parse().initialize( + keyParseApplicationId, + keyParseServerUrl, + coreStore: await CoreStoreSembastImp.getInstance()); +``` +It's possible to add other parameters to work with your instance of Parse Server:- + +```dart + await Parse().initialize( + keyApplicationId, + keyParseServerUrl, + masterKey: keyParseMasterKey, // Required for Back4App and others + clientKey: keyParseClientKey, // Required for some setups + debug: true, // When enabled, prints logs to console + liveQueryUrl: keyLiveQueryUrl, // Required if using LiveQuery + autoSendSessionId: true, // Required for authentication and ACL + securityContext: securityContext, // Again, required for some setups + coreStore: await CoreStoreSharedPrefsImp.getInstance()); // Local data storage method. Will use SharedPreferences instead of Sembast as an internal DB +``` + + +#### Early Web support +Currently this requires adding `X-Parse-Installation-Id` as an allowed header to parse-server. +When running directly via docker, set the env var `PARSE_SERVER_ALLOW_HEADERS=X-Parse-Installation-Id`. +When running via express, set [ParseServerOptions](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `allowHeaders: ['X-Parse-Installation-Id']`. + +Be aware that for web ParseInstallation does include app name, version or package identifier. + + +## Objects +You can create custom objects by calling: +```dart +var dietPlan = ParseObject('DietPlan') + ..set('Name', 'Ketogenic') + ..set('Fat', 65); +await dietPlan.save(); +``` +Or update existing object by its objectId by calling: +```dart +var dietPlan = ParseObject('DietPlan') + ..objectId = 'R5EonpUDWy' + ..set('Fat', 70); +await dietPlan.save(); +``` +Verify that the object has been successfully saved using +```dart +var response = await dietPlan.save(); +if (response.success) { + dietPlan = response.results.first; +} +``` +Types supported: + * String + * Double + * Int + * Boolean + * DateTime + * File + * Geopoint + * ParseObject/ParseUser (Pointer) + * Map + * List (all types supported) + +You then have the ability to do the following with that object: +The features available are:- + * Get + * GetAll + * Create + * Save + * Query - By object Id + * Delete + * Complex queries as shown above + * Pin + * Plenty more + * Counters + * Array Operators + +## Custom Objects +You can create your own `ParseObjects` or convert your existing objects into Parse Objects by doing the following: + +```dart +class DietPlan extends ParseObject implements ParseCloneable { + + DietPlan() : super(_keyTableName); + DietPlan.clone(): this(); + + /// Looks strangely hacky but due to Flutter not using reflection, we have to + /// mimic a clone + @override clone(Map map) => DietPlan.clone()..fromJson(map); + + static const String _keyTableName = 'Diet_Plans'; + static const String keyName = 'Name'; + + String get name => get(keyName); + set name(String name) => set(keyName, name); +} + +``` + +When receiving an `ParseObject` from the SDK, you can often provide an instance of your custom object as an copy object. +To always use your custom object class, you can register your subclass at the initialization of the SDK. +```dart +Parse().initialize( + ..., + registeredSubClassMap: { + 'Diet_Plans': () => DietPlan(), + }, + parseUserConstructor: (username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress), +); +``` +Additionally you can register `SubClasses` after the initialization of the SDK. +```dart +ParseCoreData().registerSubClass('Diet_Plans', () => DietPlan()); +ParseCoreData().registerUserSubClass((username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress)); +``` +Providing a `ParseObject` as described above should still work, even if you have registered a different `SubClass`. + +For custom file classes have a lock at [here](#File). + +## Add new values to objects +To add a variable to an object call and retrieve it, call + +```dart +dietPlan.set('RandomInt', 8); +var randomInt = dietPlan.get('RandomInt'); +``` + +## Save objects using pins +You can now save an object by calling `.pin()` on an instance of an object + +```dart +dietPlan.pin(); +``` + +and to retrieve it + +```dart +var dietPlan = DietPlan().fromPin('OBJECT ID OF OBJECT'); +``` + +## Storage +We now have 2 types of storage, secure and unsecure. We currently rely on 2 third party options: + +- SharedPreferences +- Sembast +Sembast offers secured storage, whilst SharePreferences wraps NSUserDefaults (on iOS) and SharedPreferences (on Android). + +The storage method is defined in the parameter __coreStore__ in Parse().initialize + +Check sample code for options + +## Increment Counter values in objects +Retrieve it, call + +```dart +var response = await dietPlan.increment("count", 1); + +``` +or using with save function + +```dart +dietPlan.setIncrement('count', 1); +dietPlan.setDecrement('count', 1); +var response = dietPlan.save() + +``` + +## Array Operator in objects +Retrieve it, call + +```dart +var response = await dietPlan.add("listKeywords", ["a", "a","d"]); + +var response = await dietPlan.addUnique("listKeywords", ["a", "a","d"]); + +var response = await dietPlan.remove("listKeywords", ["a"]); + +``` +or using with save function + +```dart +dietPlan.setAdd('listKeywords', ['a','a','d']); +dietPlan.setAddUnique('listKeywords', ['a','a','d']); +dietPlan.setRemove('listKeywords', ['a']); +var response = dietPlan.save() +``` + +## Queries +Once you have setup the project and initialised the instance, you can then retreive data from your server by calling: +```dart +var apiResponse = await ParseObject('ParseTableName').getAll(); + +if (apiResponse.success){ + for (var testObject in apiResponse.result) { + print(ApplicationConstants.APP_NAME + ": " + testObject.toString()); + } +} +``` +Or you can get an object by its objectId: + +```dart +var dietPlan = await DietPlan().getObject('R5EonpUDWy'); + +if (dietPlan.success) { + print(ApplicationConstants.keyAppName + ": " + (dietPlan.result as DietPlan).toString()); +} else { + print(ApplicationConstants.keyAppName + ": " + dietPlan.exception.message); +} +``` + +## Complex queries +You can create complex queries to really put your database to the test: + +```dart +var queryBuilder = QueryBuilder(DietPlan()) + ..startsWith(DietPlan.keyName, "Keto") + ..greaterThan(DietPlan.keyFat, 64) + ..lessThan(DietPlan.keyFat, 66) + ..equals(DietPlan.keyCarbs, 5); + +var response = await queryBuilder.query(); + +if (response.success) { + print(ApplicationConstants.keyAppName + ": " + ((response.results as List).first as DietPlan).toString()); +} else { + print(ApplicationConstants.keyAppName + ": " + response.exception.message); +} +``` + +if you want to find objects that match one of several queries, you can use __QueryBuilder.or__ method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do: +```dart +ParseObject playerObject = ParseObject("Player"); + +QueryBuilder lotsOfWins = + QueryBuilder(playerObject)) + ..whereGreaterThan('wins', 50); + +QueryBuilder fewWins = + QueryBuilder(playerObject) + ..whereLessThan('wins', 5); + +QueryBuilder mainQuery = QueryBuilder.or( + playerObject, + [lotsOfWins, fewWins], + ); + +var apiResponse = await mainQuery.query(); +``` + +The features available are:- + * Equals + * Contains + * LessThan + * LessThanOrEqualTo + * GreaterThan + * GreaterThanOrEqualTo + * NotEqualTo + * StartsWith + * EndsWith + * Exists + * Near + * WithinMiles + * WithinKilometers + * WithinRadians + * WithinGeoBox + * MatchesQuery + * DoesNotMatchQuery + * MatchesKeyInQuery + * DoesNotMatchKeyInQuery + * Regex + * Order + * Limit + * Skip + * Ascending + * Descending + * Plenty more! + +## Relational queries +If you want to retrieve objects where a field contains an object that matches another query, you can use the +__whereMatchesQuery__ condition. +For example, imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. +You can find comments on posts with images by doing: + +```dart +QueryBuilder queryPost = + QueryBuilder(ParseObject('Post')) + ..whereValueExists('image', true); + +QueryBuilder queryComment = + QueryBuilder(ParseObject('Comment')) + ..whereMatchesQuery('post', queryPost); + +var apiResponse = await queryComment.query(); +``` + +If you want to retrieve objects where a field contains an object that does not match another query, you can use the +__whereDoesNotMatchQuery__ condition. +Imagine you have Post class and a Comment class, where each Comment has a pointer to its parent Post. +You can find comments on posts without images by doing: + +```dart +QueryBuilder queryPost = + QueryBuilder(ParseObject('Post')) + ..whereValueExists('image', true); + +QueryBuilder queryComment = + QueryBuilder(ParseObject('Comment')) + ..whereDoesNotMatchQuery('post', queryPost); + +var apiResponse = await queryComment.query(); +``` + +You can use the __whereMatchesKeyInQuery__ method to get objects where a key matches the value of a key in a set of objects resulting from another query. For example, if you have a class containing sports teams and you store a user’s hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like:: + +```dart +QueryBuilder teamQuery = + QueryBuilder(ParseObject('Team')) + ..whereGreaterThan('winPct', 0.5); + +QueryBuilder userQuery = + QueryBuilderParseUser.forQuery()) + ..whereMatchesKeyInQuery('hometown', 'city', teamQuery); + +var apiResponse = await userQuery.query(); +``` + +Conversely, to get objects where a key does not match the value of a key in a set of objects resulting from another query, use __whereDoesNotMatchKeyInQuery__. For example, to find users whose hometown teams have losing records: + +```dart +QueryBuilder teamQuery = + QueryBuilder(ParseObject('Team')) + ..whereGreaterThan('winPct', 0.5); + +QueryBuilder losingUserQuery = + QueryBuilderParseUser.forQuery()) + ..whereDoesNotMatchKeyInQuery('hometown', 'city', teamQuery); + +var apiResponse = await losingUserQuery.query(); +``` + +To filter rows based on objectId’s from pointers in a second table, you can use dot notation: +```dart +QueryBuilder rolesOfTypeX = + QueryBuilder(ParseObject('Role')) + ..whereEqualTo('type', 'x'); + +QueryBuilder groupsWithRoleX = + QueryBuilder(ParseObject('Group'))) + ..whereMatchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX); + +var apiResponse = await groupsWithRoleX.query(); +``` + +## Counting Objects +If you only care about the number of games played by a particular player: + +```dart +QueryBuilder queryPlayers = + QueryBuilder(ParseObject('GameScore')) + ..whereEqualTo('playerName', 'Jonathan Walsh'); +var apiResponse = await queryPlayers.count(); +if (apiResponse.success && apiResponse.result != null) { + int countGames = apiResponse.count; +} +``` + +## Live Queries +This tool allows you to subscribe to a QueryBuilder you are interested in. Once subscribed, the server will notify clients +whenever a ParseObject that matches the QueryBuilder is created or updated, in real-time. + +Parse LiveQuery contains two parts, the LiveQuery server and the LiveQuery clients. In order to use live queries, you need +to set up both of them. + +The Parse Server configuration guide on the server is found here https://docs.parseplatform.org/parse-server/guide/#live-queries and is not part of this documentation. + +Initialize the Parse Live Query by entering the parameter liveQueryUrl in Parse().initialize: +```dart +Parse().initialize( + keyApplicationId, + keyParseServerUrl, + clientKey: keyParseClientKey, + debug: true, + liveQueryUrl: keyLiveQueryUrl, + autoSendSessionId: true); +``` + +Declare LiveQuery: +```dart +final LiveQuery liveQuery = LiveQuery(); +``` + +Set the QueryBuilder that will be monitored by LiveQuery: +```dart +QueryBuilder query = + QueryBuilder(ParseObject('TestAPI')) + ..whereEqualTo('intNumber', 1); +``` +__Create a subscription__ +You’ll get the LiveQuery events through this subscription. +The first time you call subscribe, we’ll try to open the WebSocket connection to the LiveQuery server for you. + +```dart +Subscription subscription = await liveQuery.client.subscribe(query); +``` + +__Event Handling__ +We define several types of events you’ll get through a subscription object: + +__Create event__ +When a new ParseObject is created and it fulfills the QueryBuilder you subscribe, you’ll get this event. +The object is the ParseObject which was created. +```dart +subscription.on(LiveQueryEvent.create, (value) { + print('*** CREATE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Update event__ +When an existing ParseObject which fulfills the QueryBuilder you subscribe is updated (The ParseObject fulfills the +QueryBuilder before and after changes), you’ll get this event. +The object is the ParseObject which was updated. Its content is the latest value of the ParseObject. +```dart +subscription.on(LiveQueryEvent.update, (value) { + print('*** UPDATE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Enter event__ +When an existing ParseObject’s old value does not fulfill the QueryBuilder but its new value fulfills the QueryBuilder, +you’ll get this event. The object is the ParseObject which enters the QueryBuilder. +Its content is the latest value of the ParseObject. +```dart +subscription.on(LiveQueryEvent.enter, (value) { + print('*** ENTER ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Leave event__ +When an existing ParseObject’s old value fulfills the QueryBuilder but its new value doesn’t fulfill the QueryBuilder, +you’ll get this event. The object is the ParseObject which leaves the QueryBuilder. +Its content is the latest value of the ParseObject. +```dart +subscription.on(LiveQueryEvent.leave, (value) { + print('*** LEAVE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Delete event__ +When an existing ParseObject which fulfills the QueryBuilder is deleted, you’ll get this event. +The object is the ParseObject which is deleted +```dart +subscription.on(LiveQueryEvent.delete, (value) { + print('*** DELETE ***: ${DateTime.now().toString()}\n $value '); + print((value as ParseObject).objectId); + print((value as ParseObject).updatedAt); + print((value as ParseObject).createdAt); + print((value as ParseObject).get('objectId')); + print((value as ParseObject).get('updatedAt')); + print((value as ParseObject).get('createdAt')); +}); +``` + +__Unsubscribe__ +If you would like to stop receiving events from a QueryBuilder, you can just unsubscribe the subscription. +After that, you won’t get any events from the subscription object and will close the WebSocket connection to the +LiveQuery server. + +```dart +liveQuery.client.unSubscribe(subscription); +``` + +__Disconnection__ +In case the client's connection to the server breaks, +LiveQuery will automatically try to reconnect. +LiveQuery will wait at increasing intervals between reconnection attempts. +By default, these intervals are set to `[0, 500, 1000, 2000, 5000, 10000]` for mobile and `[0, 500, 1000, 2000, 5000]` for web. +You can change these by providing a custom list using the `liveListRetryIntervals` parameter at `Parse.initialize()` ("-1" means "do not try to reconnect"). + +## ParseLiveList +ParseLiveList makes implementing a dynamic List as simple as possible. + +#### General Use +It ships with the ParseLiveList class itself, this class manages all elements of the list, sorts them, +keeps itself up to date and Notifies you on changes. + +ParseLiveListWidget is a widget that handles all the communication with the ParseLiveList for you. +Using ParseLiveListWidget you can create a dynamic List by just providing a QueryBuilder. + +```dart +ParseLiveListWidget( + query: query, + ); +``` +To customize the List Elements, you can provide a childBuilder. +```dart +ParseLiveListWidget( + query: query, + reverse: false, + childBuilder: + (BuildContext context, ParseLiveListElementSnapshot snapshot) { + if (snapshot.failed) { + return const Text('something went wrong!'); + } else if (snapshot.hasData) { + return ListTile( + title: Text( + snapshot.loadedData.get("text"), + ), + ); + } else { + return const ListTile( + leading: CircularProgressIndicator(), + ); + } + }, +); +``` +Similar to the standard ListView, you can provide arguments like reverse or shrinkWrap. +By providing the listLoadingElement, you can show the user something while the list is loading. +```dart +ParseLiveListWidget( + query: query, + childBuilder: childBuilder, + listLoadingElement: Center( + child: CircularProgressIndicator(), + ), +); +``` +By providing the duration argument, you can change the animation speed. +```dart +ParseLiveListWidget( + query: query, + childBuilder: childBuilder, + duration: Duration(seconds: 1), +); +``` +### included Sub-Objects +By default, ParseLiveQuery will provide you with all the objects you included in your Query like this: +```dart +queryBuilder.includeObject(/*List of all the included sub-objects*/); +``` +ParseLiveList will not listen for updates on this objects by default. +To activate listening for updates on all included objects, add `listenOnAllSubItems: true` to your ParseLiveListWidgets constructor. +If you want ParseLiveList to listen for updates on only some sub-objects, use `listeningIncludes: const [/*all the included sub-objects*/]` instead. +Just as QueryBuilder, ParseLiveList supports nested sub-objects too. + +**NOTE:** To use this features you have to enable [Live Queries](#live-queries) first. + +## Users +You can create and control users just as normal using this SDK. + +To register a user, first create one : +```dart +var user = ParseUser().create("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com"); +``` +Then have the user sign up: + +```dart +var response = await user.signUp(); +if (response.success) user = response.result; +``` +You can also login with the user: +```dart +var response = await user.login(); +if (response.success) user = response.result; +``` +You can also logout with the user: +```dart +var response = await user.logout(); +if (response.success) { + print('User logout'); +} +``` +Also, once logged in you can manage sessions tokens. This feature can be called after Parse().init() on startup to check for a logged in user. +```dart +user = ParseUser.currentUser(); +``` + +To add additional columns to the user: +```dart +var user = ParseUser("TestFlutter", "TestPassword123", "TestFlutterSDK@gmail.com") + ..set("userLocation", "FlutterLand"); +``` + +Other user features are:- + * Request Password Reset + * Verification Email Request + * Get all users + * Save + * Destroy user + * Queries + + ## Facebook, OAuth and 3rd Party Login/User + + Usually, each provider will provide their own library for logins, but the loginWith method on ParseUser accepts a name of provider, then a Map with the authentication details required. + For Facebook and the example below, we used the library provided at https://pub.dev/packages/flutter_facebook_login + + ``` + Future goToFacebookLogin() async { + final FacebookLogin facebookLogin = FacebookLogin(); + final FacebookLoginResult result = await facebookLogin.logInWithReadPermissions(['email']); + + switch (result.status) { + case FacebookLoginStatus.loggedIn: + final ParseResponse response = await ParseUser.loginWith( + 'facebook', + facebook(result.accessToken.token, + result.accessToken.userId, + result.accessToken.expires)); + + if (response.success) { + // User is logged in, test with ParseUser.currentUser() + } + break; + case FacebookLoginStatus.cancelledByUser: + // User cancelled + break; + case FacebookLoginStatus.error: + // Error + break; + } + } +``` + +For Google and the example below, we used the library provided at https://pub.dev/packages/google_sign_in + +``` +class OAuthLogin { + final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email', 'https://www.googleapis.com/auth/contacts.readonly'] ); + + sigInGoogle() async { + GoogleSignInAccount account = await _googleSignIn.signIn(); + GoogleSignInAuthentication authentication = await account.authentication; + await ParseUser.loginWith( + 'google', + google(_googleSignIn.currentUser.id, + authentication.accessToken, + authentication.idToken)); + } +} +``` + +## Security for Objects - ParseACL +For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object. +To support this type of security, each object has an access control list, implemented by the __ParseACL__ class. + +If ParseACL is not specified (with the exception of the ParseUser class) all objects are set to Public for read and write. +The simplest way to use a ParseACL is to specify that an object may only be read or written by a single user. +To create such an object, there must first be a logged in ParseUser. Then, new ParseACL(user) generates a ParseACL that +limits access to that user. An object’s ACL is updated when the object is saved, like any other property. + +```dart +ParseUser user = await ParseUser.currentUser() as ParseUser; +ParseACL parseACL = ParseACL(owner: user); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +Permissions can also be granted on a per-user basis. You can add permissions individually to a ParseACL using +__setReadAccess__ and __setWriteAccess__ +```dart +ParseUser user = await ParseUser.currentUser() as ParseUser; +ParseACL parseACL = ParseACL(); +//grant total access to current user +parseACL.setReadAccess(userId: user.objectId, allowed: true); +parseACL.setWriteAccess(userId: user.objectId, allowed: true); +//grant read access to userId: 'TjRuDjuSAO' +parseACL.setReadAccess(userId: 'TjRuDjuSAO', allowed: true); +parseACL.setWriteAccess(userId: 'TjRuDjuSAO', allowed: false); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +You can also grant permissions to all users at once using setPublicReadAccess and setPublicWriteAccess. +```dart +ParseACL parseACL = ParseACL(); +parseACL.setPublicReadAccess(allowed: true); +parseACL.setPublicWriteAccess(allowed: true); + +ParseObject parseObject = ParseObject("TestAPI"); +... +parseObject.setACL(parseACL); +var apiResponse = await parseObject.save(); +``` +Operations that are forbidden, such as deleting an object that you do not have write access to, result in a +ParseError with code 101: 'ObjectNotFound'. +For security purposes, this prevents clients from distinguishing which object ids exist but are secured, versus which +object ids do not exist at all. + +You can retrieve the ACL list of an object using: +```dart +ParseACL parseACL = parseObject.getACL(); +``` + +## Config +The SDK supports Parse Config. A map of all configs can be grabbed from the server by calling : +```dart +var response = await ParseConfig().getConfigs(); +``` + +and to add a config: +```dart +ParseConfig().addConfig('TestConfig', 'testing'); +``` + +## Cloud Functions +The SDK supports call Cloud Functions. + +Executes a cloud function that returns a ParseObject type +```dart +final ParseCloudFunction function = ParseCloudFunction('hello'); +final ParseResponse result = + await function.executeObjectFunction(); +if (result.success) { + if (result.result is ParseObject) { + final ParseObject parseObject = result.result; + print(parseObject.className); + } +} +``` + +Executes a cloud function with parameters +```dart +final ParseCloudFunction function = ParseCloudFunction('hello'); +final Map params = {'plan': 'paid'}; +function.execute(parameters: params); +``` + +## Relation + +The SDK supports Relation. + +To add relation to object: + +```dart +dietPlan.addRelation('fruits', [ParseObject("Fruits")..set("objectId", "XGadzYxnac")]); +``` + +To remove relation to object: + +```dart +dietPlan.removeRelation('fruits', [ParseObject("Fruits")..set("objectId", "XGadzYxnac")]); +``` + +To Retrive a relation instance for user, call: +```dart +final relation = dietPlan.getRelation('fruits'); +``` + +and then you can add a relation to the passed in object: +``` +relation.add(dietPlan); +final result = await user.save(); +``` + +To retrieve objects that are members of Relation field of a parent object: +```dart +QueryBuilder query = + QueryBuilder(ParseObject('Fruits')) + ..whereRelatedTo('fruits', 'DietPlan', DietPlan.objectId); +``` + +## File +There are three different file classes in this SDK: +- `ParseFileBase` is and abstract class and is the foundation of every file class that can be handled by this SDK. +- `ParseFile` (former the only file class in the SDK) extends ParseFileBase and is by default used as the file class on every platform but web. +This class uses a `File` from `dart:io` for storing the raw file. +- `ParseWebFile` is the equivalent to ParseFile used at Flutter Web. +This class uses an `Uint8List` for storing the raw file. + +These classes are used by default to represent files, but you can also build your own class extending ParseFileBase and provide a custom `ParseFileConstructor` similar to the `SubClasses`. + +Have a look at the example application for a small (non web) example. + + +```dart +//A short example for showing an image from a ParseFileBase +Widget buildImage(ParseFileBase image){ + return FutureBuilder( + future: image.download(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData) { + if (kIsWeb) { + return Image.memory((snapshot.data as ParseWebFile).file); + } else { + return Image.file((snapshot.data as ParseFile).file); + } + } else { + return CircularProgressIndicator(); + } + }, + ); +} +``` +```dart +//A short example for storing a selected picture +//libraries: image_picker (https://pub.dev/packages/image_picker), image_picker_for_web (https://pub.dev/packages/image_picker_for_web) +PickedFile pickedFile = await ImagePicker().getImage(source: ImageSource.gallery); +ParseFileBase parseFile; +if (kIsWeb) { + //Seems weird, but this lets you get the data from the selected file as an Uint8List very easily. + ParseWebFile file = ParseWebFile(null, name: null, url: pickedFile.path); + await file.download(); + parseFile = ParseWebFile(file.file, name: file.name); +} else { + parseFile = ParseFile(File(pickedFile.path)); +} +someParseObject.set("image", parseFile); +//This saves the ParseObject as well as all of its children, and the ParseFileBase is such a child. +await someParseObject.save(); +``` + +## Other Features of this library +Main: +* Installation (View the example application) +* GeoPoints (View the example application) +* Persistent storage +* Debug Mode - Logging API calls +* Manage Session ID's tokens + +User: +* Queries +* Anonymous (View the example application) +* 3rd Party Authentication + +Objects: +* Create new object +* Extend Parse Object and create local objects that can be saved and retreived +* Queries + +## Author:- +This project was authored by Phill Wiggins. You can contact me at phill.wiggins@gmail.com diff --git a/example/.gitignore b/packages/flutter/example/.gitignore similarity index 100% rename from example/.gitignore rename to packages/flutter/example/.gitignore diff --git a/example/.metadata b/packages/flutter/example/.metadata similarity index 100% rename from example/.metadata rename to packages/flutter/example/.metadata diff --git a/example/README.md b/packages/flutter/example/README.md similarity index 100% rename from example/README.md rename to packages/flutter/example/README.md diff --git a/example/android/.gitignore b/packages/flutter/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to packages/flutter/example/android/.gitignore diff --git a/example/android/.project b/packages/flutter/example/android/.project similarity index 100% rename from example/android/.project rename to packages/flutter/example/android/.project diff --git a/example/android/.settings/org.eclipse.buildship.core.prefs b/packages/flutter/example/android/.settings/org.eclipse.buildship.core.prefs similarity index 100% rename from example/android/.settings/org.eclipse.buildship.core.prefs rename to packages/flutter/example/android/.settings/org.eclipse.buildship.core.prefs diff --git a/example/android/app/.classpath b/packages/flutter/example/android/app/.classpath similarity index 100% rename from example/android/app/.classpath rename to packages/flutter/example/android/app/.classpath diff --git a/example/android/app/.project b/packages/flutter/example/android/app/.project similarity index 100% rename from example/android/app/.project rename to packages/flutter/example/android/app/.project diff --git a/example/android/app/.settings/org.eclipse.buildship.core.prefs b/packages/flutter/example/android/app/.settings/org.eclipse.buildship.core.prefs similarity index 100% rename from example/android/app/.settings/org.eclipse.buildship.core.prefs rename to packages/flutter/example/android/app/.settings/org.eclipse.buildship.core.prefs diff --git a/example/android/app/build.gradle b/packages/flutter/example/android/app/build.gradle similarity index 100% rename from example/android/app/build.gradle rename to packages/flutter/example/android/app/build.gradle diff --git a/example/android/app/src/main/AndroidManifest.xml b/packages/flutter/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to packages/flutter/example/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/gen/com/example/flutterpluginexample/BuildConfig.java b/packages/flutter/example/android/app/src/main/gen/com/example/flutterpluginexample/BuildConfig.java similarity index 100% rename from example/android/app/src/main/gen/com/example/flutterpluginexample/BuildConfig.java rename to packages/flutter/example/android/app/src/main/gen/com/example/flutterpluginexample/BuildConfig.java diff --git a/example/android/app/src/main/gen/com/example/flutterpluginexample/Manifest.java b/packages/flutter/example/android/app/src/main/gen/com/example/flutterpluginexample/Manifest.java similarity index 100% rename from example/android/app/src/main/gen/com/example/flutterpluginexample/Manifest.java rename to packages/flutter/example/android/app/src/main/gen/com/example/flutterpluginexample/Manifest.java diff --git a/example/android/app/src/main/gen/com/example/flutterpluginexample/R.java b/packages/flutter/example/android/app/src/main/gen/com/example/flutterpluginexample/R.java similarity index 100% rename from example/android/app/src/main/gen/com/example/flutterpluginexample/R.java rename to packages/flutter/example/android/app/src/main/gen/com/example/flutterpluginexample/R.java diff --git a/example/android/app/src/main/java/com/example/flutterpluginexample/MainActivity.java b/packages/flutter/example/android/app/src/main/java/com/example/flutterpluginexample/MainActivity.java similarity index 100% rename from example/android/app/src/main/java/com/example/flutterpluginexample/MainActivity.java rename to packages/flutter/example/android/app/src/main/java/com/example/flutterpluginexample/MainActivity.java diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/packages/flutter/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to packages/flutter/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/flutter/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/flutter/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/flutter/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/flutter/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/flutter/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values/styles.xml b/packages/flutter/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to packages/flutter/example/android/app/src/main/res/values/styles.xml diff --git a/example/android/build.gradle b/packages/flutter/example/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to packages/flutter/example/android/build.gradle diff --git a/example/android/gradle.properties b/packages/flutter/example/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to packages/flutter/example/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/flutter/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/flutter/example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/settings.gradle b/packages/flutter/example/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to packages/flutter/example/android/settings.gradle diff --git a/example/assets/parse.png b/packages/flutter/example/assets/parse.png similarity index 100% rename from example/assets/parse.png rename to packages/flutter/example/assets/parse.png diff --git a/example/fonts/Roboto/LICENSE.txt b/packages/flutter/example/fonts/Roboto/LICENSE.txt similarity index 100% rename from example/fonts/Roboto/LICENSE.txt rename to packages/flutter/example/fonts/Roboto/LICENSE.txt diff --git a/example/fonts/Roboto/Roboto-Black.ttf b/packages/flutter/example/fonts/Roboto/Roboto-Black.ttf similarity index 100% rename from example/fonts/Roboto/Roboto-Black.ttf rename to packages/flutter/example/fonts/Roboto/Roboto-Black.ttf diff --git a/example/fonts/Roboto/Roboto-Bold.ttf b/packages/flutter/example/fonts/Roboto/Roboto-Bold.ttf similarity index 100% rename from example/fonts/Roboto/Roboto-Bold.ttf rename to packages/flutter/example/fonts/Roboto/Roboto-Bold.ttf diff --git a/example/fonts/Roboto/Roboto-Light.ttf b/packages/flutter/example/fonts/Roboto/Roboto-Light.ttf similarity index 100% rename from example/fonts/Roboto/Roboto-Light.ttf rename to packages/flutter/example/fonts/Roboto/Roboto-Light.ttf diff --git a/example/fonts/Roboto/Roboto-Medium.ttf b/packages/flutter/example/fonts/Roboto/Roboto-Medium.ttf similarity index 100% rename from example/fonts/Roboto/Roboto-Medium.ttf rename to packages/flutter/example/fonts/Roboto/Roboto-Medium.ttf diff --git a/example/fonts/Roboto/Roboto-Regular.ttf b/packages/flutter/example/fonts/Roboto/Roboto-Regular.ttf similarity index 100% rename from example/fonts/Roboto/Roboto-Regular.ttf rename to packages/flutter/example/fonts/Roboto/Roboto-Regular.ttf diff --git a/example/fonts/Roboto/Roboto-Thin.ttf b/packages/flutter/example/fonts/Roboto/Roboto-Thin.ttf similarity index 100% rename from example/fonts/Roboto/Roboto-Thin.ttf rename to packages/flutter/example/fonts/Roboto/Roboto-Thin.ttf diff --git a/example/ios/.gitignore b/packages/flutter/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to packages/flutter/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/packages/flutter/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to packages/flutter/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/example/ios/Flutter/Debug.xcconfig b/packages/flutter/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from example/ios/Flutter/Debug.xcconfig rename to packages/flutter/example/ios/Flutter/Debug.xcconfig diff --git a/example/ios/Flutter/Release.xcconfig b/packages/flutter/example/ios/Flutter/Release.xcconfig similarity index 100% rename from example/ios/Flutter/Release.xcconfig rename to packages/flutter/example/ios/Flutter/Release.xcconfig diff --git a/example/ios/Podfile b/packages/flutter/example/ios/Podfile similarity index 100% rename from example/ios/Podfile rename to packages/flutter/example/ios/Podfile diff --git a/example/ios/Podfile.lock b/packages/flutter/example/ios/Podfile.lock similarity index 100% rename from example/ios/Podfile.lock rename to packages/flutter/example/ios/Podfile.lock diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/packages/flutter/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to packages/flutter/example/ios/Runner.xcodeproj/project.pbxproj diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/flutter/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/flutter/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/flutter/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/flutter/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.h b/packages/flutter/example/ios/Runner/AppDelegate.h similarity index 100% rename from example/ios/Runner/AppDelegate.h rename to packages/flutter/example/ios/Runner/AppDelegate.h diff --git a/example/ios/Runner/AppDelegate.m b/packages/flutter/example/ios/Runner/AppDelegate.m similarity index 100% rename from example/ios/Runner/AppDelegate.m rename to packages/flutter/example/ios/Runner/AppDelegate.m diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/flutter/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/flutter/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/packages/flutter/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to packages/flutter/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/packages/flutter/example/ios/Runner/Info.plist similarity index 100% rename from example/ios/Runner/Info.plist rename to packages/flutter/example/ios/Runner/Info.plist diff --git a/example/ios/Runner/main.m b/packages/flutter/example/ios/Runner/main.m similarity index 100% rename from example/ios/Runner/main.m rename to packages/flutter/example/ios/Runner/main.m diff --git a/example/lib/data/base/api_error.dart b/packages/flutter/example/lib/data/base/api_error.dart similarity index 100% rename from example/lib/data/base/api_error.dart rename to packages/flutter/example/lib/data/base/api_error.dart diff --git a/example/lib/data/base/api_response.dart b/packages/flutter/example/lib/data/base/api_response.dart similarity index 100% rename from example/lib/data/base/api_response.dart rename to packages/flutter/example/lib/data/base/api_response.dart diff --git a/example/lib/data/model/day.dart b/packages/flutter/example/lib/data/model/day.dart similarity index 100% rename from example/lib/data/model/day.dart rename to packages/flutter/example/lib/data/model/day.dart diff --git a/example/lib/data/model/diet_plan.dart b/packages/flutter/example/lib/data/model/diet_plan.dart similarity index 100% rename from example/lib/data/model/diet_plan.dart rename to packages/flutter/example/lib/data/model/diet_plan.dart diff --git a/example/lib/data/model/user.dart b/packages/flutter/example/lib/data/model/user.dart similarity index 100% rename from example/lib/data/model/user.dart rename to packages/flutter/example/lib/data/model/user.dart diff --git a/example/lib/data/repositories/diet_plan/contract_provider_diet_plan.dart b/packages/flutter/example/lib/data/repositories/diet_plan/contract_provider_diet_plan.dart similarity index 100% rename from example/lib/data/repositories/diet_plan/contract_provider_diet_plan.dart rename to packages/flutter/example/lib/data/repositories/diet_plan/contract_provider_diet_plan.dart diff --git a/example/lib/data/repositories/diet_plan/provider_api_diet_plan.dart b/packages/flutter/example/lib/data/repositories/diet_plan/provider_api_diet_plan.dart similarity index 100% rename from example/lib/data/repositories/diet_plan/provider_api_diet_plan.dart rename to packages/flutter/example/lib/data/repositories/diet_plan/provider_api_diet_plan.dart diff --git a/example/lib/data/repositories/diet_plan/provider_db_diet_plan.dart b/packages/flutter/example/lib/data/repositories/diet_plan/provider_db_diet_plan.dart similarity index 100% rename from example/lib/data/repositories/diet_plan/provider_db_diet_plan.dart rename to packages/flutter/example/lib/data/repositories/diet_plan/provider_db_diet_plan.dart diff --git a/example/lib/data/repositories/diet_plan/repository_diet_plan.dart b/packages/flutter/example/lib/data/repositories/diet_plan/repository_diet_plan.dart similarity index 100% rename from example/lib/data/repositories/diet_plan/repository_diet_plan.dart rename to packages/flutter/example/lib/data/repositories/diet_plan/repository_diet_plan.dart diff --git a/example/lib/data/repositories/user/contract_provider_user.dart b/packages/flutter/example/lib/data/repositories/user/contract_provider_user.dart similarity index 100% rename from example/lib/data/repositories/user/contract_provider_user.dart rename to packages/flutter/example/lib/data/repositories/user/contract_provider_user.dart diff --git a/example/lib/data/repositories/user/provider_api_user.dart b/packages/flutter/example/lib/data/repositories/user/provider_api_user.dart similarity index 100% rename from example/lib/data/repositories/user/provider_api_user.dart rename to packages/flutter/example/lib/data/repositories/user/provider_api_user.dart diff --git a/example/lib/data/repositories/user/provider_db_user.dart b/packages/flutter/example/lib/data/repositories/user/provider_db_user.dart similarity index 100% rename from example/lib/data/repositories/user/provider_db_user.dart rename to packages/flutter/example/lib/data/repositories/user/provider_db_user.dart diff --git a/example/lib/data/repositories/user/repository_user.dart b/packages/flutter/example/lib/data/repositories/user/repository_user.dart similarity index 100% rename from example/lib/data/repositories/user/repository_user.dart rename to packages/flutter/example/lib/data/repositories/user/repository_user.dart diff --git a/example/lib/domain/constants/application_constants.dart b/packages/flutter/example/lib/domain/constants/application_constants.dart similarity index 100% rename from example/lib/domain/constants/application_constants.dart rename to packages/flutter/example/lib/domain/constants/application_constants.dart diff --git a/example/lib/domain/utils/collection_utils.dart b/packages/flutter/example/lib/domain/utils/collection_utils.dart similarity index 100% rename from example/lib/domain/utils/collection_utils.dart rename to packages/flutter/example/lib/domain/utils/collection_utils.dart diff --git a/example/lib/domain/utils/db_utils.dart b/packages/flutter/example/lib/domain/utils/db_utils.dart similarity index 100% rename from example/lib/domain/utils/db_utils.dart rename to packages/flutter/example/lib/domain/utils/db_utils.dart diff --git a/example/lib/main.dart b/packages/flutter/example/lib/main.dart similarity index 99% rename from example/lib/main.dart rename to packages/flutter/example/lib/main.dart index aee8d831e..22c7bdae6 100644 --- a/example/lib/main.dart +++ b/packages/flutter/example/lib/main.dart @@ -11,7 +11,7 @@ import 'package:flutter_plugin_example/data/repositories/user/repository_user.da import 'package:flutter_plugin_example/domain/constants/application_constants.dart'; import 'package:flutter_plugin_example/domain/utils/db_utils.dart'; import 'package:flutter_plugin_example/pages/decision_page.dart'; -import 'package:parse_server_sdk/parse_server_sdk.dart'; +import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; void main() { _setTargetPlatformForDesktop(); diff --git a/example/lib/pages/decision_page.dart b/packages/flutter/example/lib/pages/decision_page.dart similarity index 100% rename from example/lib/pages/decision_page.dart rename to packages/flutter/example/lib/pages/decision_page.dart diff --git a/example/lib/pages/home_page.dart b/packages/flutter/example/lib/pages/home_page.dart similarity index 100% rename from example/lib/pages/home_page.dart rename to packages/flutter/example/lib/pages/home_page.dart diff --git a/example/lib/pages/login_page.dart b/packages/flutter/example/lib/pages/login_page.dart similarity index 100% rename from example/lib/pages/login_page.dart rename to packages/flutter/example/lib/pages/login_page.dart diff --git a/example/linux/.gitignore b/packages/flutter/example/linux/.gitignore similarity index 100% rename from example/linux/.gitignore rename to packages/flutter/example/linux/.gitignore diff --git a/example/linux/Makefile b/packages/flutter/example/linux/Makefile similarity index 100% rename from example/linux/Makefile rename to packages/flutter/example/linux/Makefile diff --git a/example/linux/flutter_embedder_example.cc b/packages/flutter/example/linux/flutter_embedder_example.cc similarity index 100% rename from example/linux/flutter_embedder_example.cc rename to packages/flutter/example/linux/flutter_embedder_example.cc diff --git a/example/macos/.gitignore b/packages/flutter/example/macos/.gitignore similarity index 100% rename from example/macos/.gitignore rename to packages/flutter/example/macos/.gitignore diff --git a/example/macos/AppDelegate.swift b/packages/flutter/example/macos/AppDelegate.swift similarity index 100% rename from example/macos/AppDelegate.swift rename to packages/flutter/example/macos/AppDelegate.swift diff --git a/example/macos/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/flutter/example/macos/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/macos/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/flutter/example/macos/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/macos/Base.lproj/MainMenu.xib b/packages/flutter/example/macos/Base.lproj/MainMenu.xib similarity index 100% rename from example/macos/Base.lproj/MainMenu.xib rename to packages/flutter/example/macos/Base.lproj/MainMenu.xib diff --git a/example/macos/ExampleWindow.swift b/packages/flutter/example/macos/ExampleWindow.swift similarity index 100% rename from example/macos/ExampleWindow.swift rename to packages/flutter/example/macos/ExampleWindow.swift diff --git a/example/macos/Flutter/Debug.xcconfig b/packages/flutter/example/macos/Flutter/Debug.xcconfig similarity index 100% rename from example/macos/Flutter/Debug.xcconfig rename to packages/flutter/example/macos/Flutter/Debug.xcconfig diff --git a/example/macos/Flutter/Release.xcconfig b/packages/flutter/example/macos/Flutter/Release.xcconfig similarity index 100% rename from example/macos/Flutter/Release.xcconfig rename to packages/flutter/example/macos/Flutter/Release.xcconfig diff --git a/example/macos/Info.plist b/packages/flutter/example/macos/Info.plist similarity index 100% rename from example/macos/Info.plist rename to packages/flutter/example/macos/Info.plist diff --git a/example/macos/PluginRegistrant.h b/packages/flutter/example/macos/PluginRegistrant.h similarity index 100% rename from example/macos/PluginRegistrant.h rename to packages/flutter/example/macos/PluginRegistrant.h diff --git a/example/macos/PluginRegistrant.m b/packages/flutter/example/macos/PluginRegistrant.m similarity index 100% rename from example/macos/PluginRegistrant.m rename to packages/flutter/example/macos/PluginRegistrant.m diff --git a/packages/flutter/example/macos/Podfile b/packages/flutter/example/macos/Podfile new file mode 100644 index 000000000..d60ec7102 --- /dev/null +++ b/packages/flutter/example/macos/Podfile @@ -0,0 +1,82 @@ +platform :osx, '10.11' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +def pubspec_supports_macos(file) + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return false; + end + File.foreach(file_abs_path) { |line| + return true if line =~ /^\s*macos:/ + } + return false +end + +target 'Runner' do + use_frameworks! + use_modular_headers! + + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + ephemeral_dir = File.join('Flutter', 'ephemeral') + symlink_dir = File.join(ephemeral_dir, '.symlinks') + symlink_plugins_dir = File.join(symlink_dir, 'plugins') + system("rm -rf #{symlink_dir}") + system("mkdir -p #{symlink_plugins_dir}") + + # Flutter Pods + generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig')) + if generated_xcconfig.empty? + puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcconfig.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join(symlink_dir, 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join(symlink_plugins_dir, p[:name]) + File.symlink(p[:path], symlink) + if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml')) + pod p[:name], :path => File.join(symlink, 'macos') + end + } +end + +# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. +install! 'cocoapods', :disable_input_output_paths => true diff --git a/example/macos/Runner-Bridging-Header.h b/packages/flutter/example/macos/Runner-Bridging-Header.h similarity index 100% rename from example/macos/Runner-Bridging-Header.h rename to packages/flutter/example/macos/Runner-Bridging-Header.h diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/packages/flutter/example/macos/Runner.xcodeproj/project.pbxproj similarity index 100% rename from example/macos/Runner.xcodeproj/project.pbxproj rename to packages/flutter/example/macos/Runner.xcodeproj/project.pbxproj diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/flutter/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/flutter/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/flutter/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/flutter/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/macos/name_output.sh b/packages/flutter/example/macos/name_output.sh similarity index 100% rename from example/macos/name_output.sh rename to packages/flutter/example/macos/name_output.sh diff --git a/example/pubspec.yaml b/packages/flutter/example/pubspec.yaml similarity index 98% rename from example/pubspec.yaml rename to packages/flutter/example/pubspec.yaml index 889345e65..666274921 100644 --- a/example/pubspec.yaml +++ b/packages/flutter/example/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: path_provider: ^1.6.14 dev_dependencies: - parse_server_sdk: + parse_server_sdk_flutter: path: ../ flutter_test: sdk: flutter diff --git a/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart similarity index 100% rename from example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart rename to packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_api_test.dart diff --git a/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart similarity index 100% rename from example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart rename to packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_db_test.dart diff --git a/example/test/data/repository/diet_plan/repository_diet_plan_test.dart b/packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_test.dart similarity index 100% rename from example/test/data/repository/diet_plan/repository_diet_plan_test.dart rename to packages/flutter/example/test/data/repository/diet_plan/repository_diet_plan_test.dart diff --git a/example/test/data/repository/repository_mock_utils.dart b/packages/flutter/example/test/data/repository/repository_mock_utils.dart similarity index 100% rename from example/test/data/repository/repository_mock_utils.dart rename to packages/flutter/example/test/data/repository/repository_mock_utils.dart diff --git a/example/windows/.gitignore b/packages/flutter/example/windows/.gitignore similarity index 100% rename from example/windows/.gitignore rename to packages/flutter/example/windows/.gitignore diff --git a/example/windows/Runner.sln b/packages/flutter/example/windows/Runner.sln similarity index 100% rename from example/windows/Runner.sln rename to packages/flutter/example/windows/Runner.sln diff --git a/example/windows/Runner.vcxproj b/packages/flutter/example/windows/Runner.vcxproj similarity index 100% rename from example/windows/Runner.vcxproj rename to packages/flutter/example/windows/Runner.vcxproj diff --git a/example/windows/Runner.vcxproj.filters b/packages/flutter/example/windows/Runner.vcxproj.filters similarity index 100% rename from example/windows/Runner.vcxproj.filters rename to packages/flutter/example/windows/Runner.vcxproj.filters diff --git a/example/windows/build.bat b/packages/flutter/example/windows/build.bat similarity index 100% rename from example/windows/build.bat rename to packages/flutter/example/windows/build.bat diff --git a/example/windows/find_vcvars.dart b/packages/flutter/example/windows/find_vcvars.dart similarity index 100% rename from example/windows/find_vcvars.dart rename to packages/flutter/example/windows/find_vcvars.dart diff --git a/example/windows/flutter_embedder_example.cpp b/packages/flutter/example/windows/flutter_embedder_example.cpp similarity index 100% rename from example/windows/flutter_embedder_example.cpp rename to packages/flutter/example/windows/flutter_embedder_example.cpp diff --git a/example/windows/generate_props.dart b/packages/flutter/example/windows/generate_props.dart similarity index 100% rename from example/windows/generate_props.dart rename to packages/flutter/example/windows/generate_props.dart diff --git a/example/windows/name_output.bat b/packages/flutter/example/windows/name_output.bat similarity index 100% rename from example/windows/name_output.bat rename to packages/flutter/example/windows/name_output.bat diff --git a/example/windows/scripts/bundle_assets_and_deps.bat b/packages/flutter/example/windows/scripts/bundle_assets_and_deps.bat similarity index 100% rename from example/windows/scripts/bundle_assets_and_deps.bat rename to packages/flutter/example/windows/scripts/bundle_assets_and_deps.bat diff --git a/example/windows/scripts/prepare_dependencies.bat b/packages/flutter/example/windows/scripts/prepare_dependencies.bat similarity index 100% rename from example/windows/scripts/prepare_dependencies.bat rename to packages/flutter/example/windows/scripts/prepare_dependencies.bat diff --git a/example_livelist/.gitignore b/packages/flutter/example_livelist/.gitignore similarity index 100% rename from example_livelist/.gitignore rename to packages/flutter/example_livelist/.gitignore diff --git a/example_livelist/.metadata b/packages/flutter/example_livelist/.metadata similarity index 100% rename from example_livelist/.metadata rename to packages/flutter/example_livelist/.metadata diff --git a/example_livelist/README.md b/packages/flutter/example_livelist/README.md similarity index 100% rename from example_livelist/README.md rename to packages/flutter/example_livelist/README.md diff --git a/example_livelist/android/.gitignore b/packages/flutter/example_livelist/android/.gitignore similarity index 100% rename from example_livelist/android/.gitignore rename to packages/flutter/example_livelist/android/.gitignore diff --git a/example_livelist/android/app/build.gradle b/packages/flutter/example_livelist/android/app/build.gradle similarity index 100% rename from example_livelist/android/app/build.gradle rename to packages/flutter/example_livelist/android/app/build.gradle diff --git a/example_livelist/android/app/src/debug/AndroidManifest.xml b/packages/flutter/example_livelist/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example_livelist/android/app/src/debug/AndroidManifest.xml rename to packages/flutter/example_livelist/android/app/src/debug/AndroidManifest.xml diff --git a/example_livelist/android/app/src/main/AndroidManifest.xml b/packages/flutter/example_livelist/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example_livelist/android/app/src/main/AndroidManifest.xml rename to packages/flutter/example_livelist/android/app/src/main/AndroidManifest.xml diff --git a/example_livelist/android/app/src/main/kotlin/com/example/example_livelist/MainActivity.kt b/packages/flutter/example_livelist/android/app/src/main/kotlin/com/example/example_livelist/MainActivity.kt similarity index 100% rename from example_livelist/android/app/src/main/kotlin/com/example/example_livelist/MainActivity.kt rename to packages/flutter/example_livelist/android/app/src/main/kotlin/com/example/example_livelist/MainActivity.kt diff --git a/example_livelist/android/app/src/main/kotlin/com/example/flutterpluginexample_livelist/MainActivity.kt b/packages/flutter/example_livelist/android/app/src/main/kotlin/com/example/flutterpluginexample_livelist/MainActivity.kt similarity index 100% rename from example_livelist/android/app/src/main/kotlin/com/example/flutterpluginexample_livelist/MainActivity.kt rename to packages/flutter/example_livelist/android/app/src/main/kotlin/com/example/flutterpluginexample_livelist/MainActivity.kt diff --git a/example_livelist/android/app/src/main/res/drawable/launch_background.xml b/packages/flutter/example_livelist/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example_livelist/android/app/src/main/res/drawable/launch_background.xml rename to packages/flutter/example_livelist/android/app/src/main/res/drawable/launch_background.xml diff --git a/example_livelist/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/flutter/example_livelist/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example_livelist/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/flutter/example_livelist/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example_livelist/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/flutter/example_livelist/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example_livelist/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/flutter/example_livelist/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example_livelist/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/flutter/example_livelist/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example_livelist/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/flutter/example_livelist/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example_livelist/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/flutter/example_livelist/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example_livelist/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/flutter/example_livelist/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example_livelist/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/flutter/example_livelist/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example_livelist/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/flutter/example_livelist/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example_livelist/android/app/src/main/res/values/styles.xml b/packages/flutter/example_livelist/android/app/src/main/res/values/styles.xml similarity index 100% rename from example_livelist/android/app/src/main/res/values/styles.xml rename to packages/flutter/example_livelist/android/app/src/main/res/values/styles.xml diff --git a/example_livelist/android/app/src/profile/AndroidManifest.xml b/packages/flutter/example_livelist/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example_livelist/android/app/src/profile/AndroidManifest.xml rename to packages/flutter/example_livelist/android/app/src/profile/AndroidManifest.xml diff --git a/example_livelist/android/build.gradle b/packages/flutter/example_livelist/android/build.gradle similarity index 100% rename from example_livelist/android/build.gradle rename to packages/flutter/example_livelist/android/build.gradle diff --git a/example_livelist/android/gradle.properties b/packages/flutter/example_livelist/android/gradle.properties similarity index 100% rename from example_livelist/android/gradle.properties rename to packages/flutter/example_livelist/android/gradle.properties diff --git a/example_livelist/android/gradle/wrapper/gradle-wrapper.properties b/packages/flutter/example_livelist/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example_livelist/android/gradle/wrapper/gradle-wrapper.properties rename to packages/flutter/example_livelist/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example_livelist/android/settings.gradle b/packages/flutter/example_livelist/android/settings.gradle similarity index 100% rename from example_livelist/android/settings.gradle rename to packages/flutter/example_livelist/android/settings.gradle diff --git a/example_livelist/assets/parse.png b/packages/flutter/example_livelist/assets/parse.png similarity index 100% rename from example_livelist/assets/parse.png rename to packages/flutter/example_livelist/assets/parse.png diff --git a/example_livelist/fonts/Roboto/LICENSE.txt b/packages/flutter/example_livelist/fonts/Roboto/LICENSE.txt similarity index 100% rename from example_livelist/fonts/Roboto/LICENSE.txt rename to packages/flutter/example_livelist/fonts/Roboto/LICENSE.txt diff --git a/example_livelist/fonts/Roboto/Roboto-Black.ttf b/packages/flutter/example_livelist/fonts/Roboto/Roboto-Black.ttf similarity index 100% rename from example_livelist/fonts/Roboto/Roboto-Black.ttf rename to packages/flutter/example_livelist/fonts/Roboto/Roboto-Black.ttf diff --git a/example_livelist/fonts/Roboto/Roboto-Bold.ttf b/packages/flutter/example_livelist/fonts/Roboto/Roboto-Bold.ttf similarity index 100% rename from example_livelist/fonts/Roboto/Roboto-Bold.ttf rename to packages/flutter/example_livelist/fonts/Roboto/Roboto-Bold.ttf diff --git a/example_livelist/fonts/Roboto/Roboto-Light.ttf b/packages/flutter/example_livelist/fonts/Roboto/Roboto-Light.ttf similarity index 100% rename from example_livelist/fonts/Roboto/Roboto-Light.ttf rename to packages/flutter/example_livelist/fonts/Roboto/Roboto-Light.ttf diff --git a/example_livelist/fonts/Roboto/Roboto-Medium.ttf b/packages/flutter/example_livelist/fonts/Roboto/Roboto-Medium.ttf similarity index 100% rename from example_livelist/fonts/Roboto/Roboto-Medium.ttf rename to packages/flutter/example_livelist/fonts/Roboto/Roboto-Medium.ttf diff --git a/example_livelist/fonts/Roboto/Roboto-Regular.ttf b/packages/flutter/example_livelist/fonts/Roboto/Roboto-Regular.ttf similarity index 100% rename from example_livelist/fonts/Roboto/Roboto-Regular.ttf rename to packages/flutter/example_livelist/fonts/Roboto/Roboto-Regular.ttf diff --git a/example_livelist/fonts/Roboto/Roboto-Thin.ttf b/packages/flutter/example_livelist/fonts/Roboto/Roboto-Thin.ttf similarity index 100% rename from example_livelist/fonts/Roboto/Roboto-Thin.ttf rename to packages/flutter/example_livelist/fonts/Roboto/Roboto-Thin.ttf diff --git a/example_livelist/ios/.gitignore b/packages/flutter/example_livelist/ios/.gitignore similarity index 100% rename from example_livelist/ios/.gitignore rename to packages/flutter/example_livelist/ios/.gitignore diff --git a/example_livelist/ios/Flutter/AppFrameworkInfo.plist b/packages/flutter/example_livelist/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example_livelist/ios/Flutter/AppFrameworkInfo.plist rename to packages/flutter/example_livelist/ios/Flutter/AppFrameworkInfo.plist diff --git a/example_livelist/ios/Flutter/Debug.xcconfig b/packages/flutter/example_livelist/ios/Flutter/Debug.xcconfig similarity index 100% rename from example_livelist/ios/Flutter/Debug.xcconfig rename to packages/flutter/example_livelist/ios/Flutter/Debug.xcconfig diff --git a/example_livelist/ios/Flutter/Release.xcconfig b/packages/flutter/example_livelist/ios/Flutter/Release.xcconfig similarity index 100% rename from example_livelist/ios/Flutter/Release.xcconfig rename to packages/flutter/example_livelist/ios/Flutter/Release.xcconfig diff --git a/example_livelist/ios/Podfile b/packages/flutter/example_livelist/ios/Podfile similarity index 100% rename from example_livelist/ios/Podfile rename to packages/flutter/example_livelist/ios/Podfile diff --git a/example_livelist/ios/Podfile.lock b/packages/flutter/example_livelist/ios/Podfile.lock similarity index 100% rename from example_livelist/ios/Podfile.lock rename to packages/flutter/example_livelist/ios/Podfile.lock diff --git a/example_livelist/ios/Runner.xcodeproj/project.pbxproj b/packages/flutter/example_livelist/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from example_livelist/ios/Runner.xcodeproj/project.pbxproj rename to packages/flutter/example_livelist/ios/Runner.xcodeproj/project.pbxproj diff --git a/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/flutter/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example_livelist/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/flutter/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/flutter/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/flutter/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/flutter/example_livelist/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example_livelist/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter/example_livelist/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example_livelist/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/flutter/example_livelist/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example_livelist/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/flutter/example_livelist/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example_livelist/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/flutter/example_livelist/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example_livelist/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter/example_livelist/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example_livelist/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/flutter/example_livelist/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example_livelist/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/flutter/example_livelist/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example_livelist/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/flutter/example_livelist/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example_livelist/ios/Runner/AppDelegate.swift b/packages/flutter/example_livelist/ios/Runner/AppDelegate.swift similarity index 100% rename from example_livelist/ios/Runner/AppDelegate.swift rename to packages/flutter/example_livelist/ios/Runner/AppDelegate.swift diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/flutter/example_livelist/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example_livelist/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/flutter/example_livelist/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example_livelist/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/flutter/example_livelist/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example_livelist/ios/Runner/Base.lproj/Main.storyboard b/packages/flutter/example_livelist/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example_livelist/ios/Runner/Base.lproj/Main.storyboard rename to packages/flutter/example_livelist/ios/Runner/Base.lproj/Main.storyboard diff --git a/example_livelist/ios/Runner/Info.plist b/packages/flutter/example_livelist/ios/Runner/Info.plist similarity index 100% rename from example_livelist/ios/Runner/Info.plist rename to packages/flutter/example_livelist/ios/Runner/Info.plist diff --git a/example_livelist/ios/Runner/Runner-Bridging-Header.h b/packages/flutter/example_livelist/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from example_livelist/ios/Runner/Runner-Bridging-Header.h rename to packages/flutter/example_livelist/ios/Runner/Runner-Bridging-Header.h diff --git a/example_livelist/lib/application_constants.dart b/packages/flutter/example_livelist/lib/application_constants.dart similarity index 100% rename from example_livelist/lib/application_constants.dart rename to packages/flutter/example_livelist/lib/application_constants.dart diff --git a/example_livelist/lib/main.dart b/packages/flutter/example_livelist/lib/main.dart similarity index 98% rename from example_livelist/lib/main.dart rename to packages/flutter/example_livelist/lib/main.dart index 14b569689..d27d57330 100644 --- a/example_livelist/lib/main.dart +++ b/packages/flutter/example_livelist/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:parse_server_sdk/parse_server_sdk.dart'; +import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'application_constants.dart'; diff --git a/example_livelist/pubspec.yaml b/packages/flutter/example_livelist/pubspec.yaml similarity index 98% rename from example_livelist/pubspec.yaml rename to packages/flutter/example_livelist/pubspec.yaml index 05bc5d4d5..339842141 100644 --- a/example_livelist/pubspec.yaml +++ b/packages/flutter/example_livelist/pubspec.yaml @@ -6,7 +6,7 @@ version: 1.0.0+1 dependencies: flutter: sdk: flutter - parse_server_sdk: + parse_server_sdk_flutter: path: ../ cupertino_icons: ^0.1.2 diff --git a/example_livelist/web/favicon.png b/packages/flutter/example_livelist/web/favicon.png similarity index 100% rename from example_livelist/web/favicon.png rename to packages/flutter/example_livelist/web/favicon.png diff --git a/example_livelist/web/icons/Icon-192.png b/packages/flutter/example_livelist/web/icons/Icon-192.png similarity index 100% rename from example_livelist/web/icons/Icon-192.png rename to packages/flutter/example_livelist/web/icons/Icon-192.png diff --git a/example_livelist/web/icons/Icon-512.png b/packages/flutter/example_livelist/web/icons/Icon-512.png similarity index 100% rename from example_livelist/web/icons/Icon-512.png rename to packages/flutter/example_livelist/web/icons/Icon-512.png diff --git a/example_livelist/web/index.html b/packages/flutter/example_livelist/web/index.html similarity index 100% rename from example_livelist/web/index.html rename to packages/flutter/example_livelist/web/index.html diff --git a/example_livelist/web/manifest.json b/packages/flutter/example_livelist/web/manifest.json similarity index 100% rename from example_livelist/web/manifest.json rename to packages/flutter/example_livelist/web/manifest.json diff --git a/lib/generated/i18n.dart b/packages/flutter/lib/generated/i18n.dart similarity index 100% rename from lib/generated/i18n.dart rename to packages/flutter/lib/generated/i18n.dart diff --git a/lib/parse_server_sdk.dart b/packages/flutter/lib/parse_server_sdk.dart similarity index 91% rename from lib/parse_server_sdk.dart rename to packages/flutter/lib/parse_server_sdk.dart index fa4fd2b21..c8d4d6088 100644 --- a/lib/parse_server_sdk.dart +++ b/packages/flutter/lib/parse_server_sdk.dart @@ -1,20 +1,24 @@ +library flutter_parse_sdk_flutter; + import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; import 'package:connectivity/connectivity.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:package_info/package_info.dart'; +import 'package:parse_server_sdk/parse_server_sdk.dart' as sdk; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:sembast/sembast.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -import 'parse_server_sdk_dart.dart' as sdk; -import 'src/storage/core_store_sp_impl.dart'; +export 'package:parse_server_sdk/parse_server_sdk.dart' + hide Parse, CoreStoreSembastImp; -export 'parse_server_sdk_dart.dart' hide Parse, CoreStoreSembastImp; -export 'src/storage/core_store_sp_impl.dart'; -export 'src/utils/parse_live_list_flutter.dart'; +part 'src/storage/core_store_sp_impl.dart'; +part 'src/utils/parse_live_list.dart'; class Parse extends sdk.Parse with WidgetsBindingObserver @@ -57,7 +61,8 @@ class Parse extends sdk.Parse String fileDirectory, Stream appResumedStream, }) async { - if (!sdk.parseIsWeb && (appName == null || appVersion == null || appPackageName == null)) { + if (!sdk.parseIsWeb && + (appName == null || appVersion == null || appPackageName == null)) { final PackageInfo packageInfo = await PackageInfo.fromPlatform(); appName ??= packageInfo.appName; appVersion ??= packageInfo.version; @@ -135,7 +140,8 @@ class CoreStoreSembastImp implements sdk.CoreStoreSembastImp { static sdk.CoreStoreSembastImp _sembastImp; - static Future getInstance({DatabaseFactory factory, String password}) async { + static Future getInstance( + {DatabaseFactory factory, String password}) async { if (_sembastImp == null) { String dbDirectory = ''; if (!sdk.parseIsWeb && diff --git a/lib/src/storage/core_store_sp_impl.dart b/packages/flutter/lib/src/storage/core_store_sp_impl.dart similarity index 90% rename from lib/src/storage/core_store_sp_impl.dart rename to packages/flutter/lib/src/storage/core_store_sp_impl.dart index e41693c43..63f54de44 100644 --- a/lib/src/storage/core_store_sp_impl.dart +++ b/packages/flutter/lib/src/storage/core_store_sp_impl.dart @@ -1,12 +1,11 @@ -import 'package:parse_server_sdk/parse_server_sdk_dart.dart'; -import 'package:shared_preferences/shared_preferences.dart'; +part of flutter_parse_sdk_flutter; -class CoreStoreSharedPrefsImp implements CoreStore { +class CoreStoreSharedPrefsImp implements sdk.CoreStore { CoreStoreSharedPrefsImp._internal(this._store); static CoreStoreSharedPrefsImp _instance; - static Future getInstance( + static Future getInstance( {SharedPreferences store, String password = 'flutter_sdk'}) async { if (_instance == null) { store ??= await SharedPreferences.getInstance(); diff --git a/lib/src/utils/parse_live_list_flutter.dart b/packages/flutter/lib/src/utils/parse_live_list.dart similarity index 76% rename from lib/src/utils/parse_live_list_flutter.dart rename to packages/flutter/lib/src/utils/parse_live_list.dart index 1d6eb394a..a4674dac5 100644 --- a/lib/src/utils/parse_live_list_flutter.dart +++ b/packages/flutter/lib/src/utils/parse_live_list.dart @@ -1,12 +1,9 @@ -import 'dart:async'; +part of flutter_parse_sdk_flutter; -import 'package:flutter/material.dart'; -import 'package:parse_server_sdk/parse_server_sdk_dart.dart'; +typedef ChildBuilder = Widget Function( + BuildContext context, sdk.ParseLiveListElementSnapshot snapshot); -typedef ChildBuilder = Widget Function( - BuildContext context, ParseLiveListElementSnapshot snapshot); - -class ParseLiveListWidget extends StatefulWidget { +class ParseLiveListWidget extends StatefulWidget { const ParseLiveListWidget({ Key key, @required this.query, @@ -26,7 +23,7 @@ class ParseLiveListWidget extends StatefulWidget { this.lazyLoading = true, }) : super(key: key); - final QueryBuilder query; + final sdk.QueryBuilder query; final Widget listLoadingElement; final Duration duration; final ScrollPhysics scrollPhysics; @@ -55,15 +52,15 @@ class ParseLiveListWidget extends StatefulWidget { lazyLoading: lazyLoading, ); - static Widget defaultChildBuilder( - BuildContext context, ParseLiveListElementSnapshot snapshot) { + static Widget defaultChildBuilder( + BuildContext context, sdk.ParseLiveListElementSnapshot snapshot) { Widget child; if (snapshot.failed) { child = const Text('something went wrong!'); } else if (snapshot.hasData) { child = ListTile( title: Text( - snapshot.loadedData.get(keyVarObjectId), + snapshot.loadedData.get(sdk.keyVarObjectId), ), ); } else { @@ -75,7 +72,7 @@ class ParseLiveListWidget extends StatefulWidget { } } -class _ParseLiveListWidgetState +class _ParseLiveListWidgetState extends State> { _ParseLiveListWidgetState( {@required this.query, @@ -83,26 +80,27 @@ class _ParseLiveListWidgetState bool listenOnAllSubItems, List listeningIncludes, bool lazyLoading = true}) { - ParseLiveList.create( + sdk.ParseLiveList.create( query, listenOnAllSubItems: listenOnAllSubItems, listeningIncludes: listeningIncludes, lazyLoading: lazyLoading, - ).then((ParseLiveList value) { + ).then((sdk.ParseLiveList value) { setState(() { _liveList = value; - _liveList.stream.listen((ParseLiveListEvent event) { - if (event is ParseLiveListAddEvent) { + _liveList.stream + .listen((sdk.ParseLiveListEvent event) { + if (event is sdk.ParseLiveListAddEvent) { if (_animatedListKey.currentState != null) _animatedListKey.currentState .insertItem(event.index, duration: widget.duration); - } else if (event is ParseLiveListDeleteEvent) { + } else if (event is sdk.ParseLiveListDeleteEvent) { _animatedListKey.currentState.removeItem( event.index, (BuildContext context, Animation animation) => ParseLiveListElementWidget( key: ValueKey(event.object?.get( - keyVarObjectId, + sdk.keyVarObjectId, defaultValue: 'removingItem')), childBuilder: widget.childBuilder ?? ParseLiveListWidget.defaultChildBuilder, @@ -117,8 +115,8 @@ class _ParseLiveListWidgetState }); } - final QueryBuilder query; - ParseLiveList _liveList; + final sdk.QueryBuilder query; + sdk.ParseLiveList _liveList; final GlobalKey _animatedListKey = GlobalKey(); final ChildBuilder removedItemBuilder; @@ -164,7 +162,8 @@ class _ParseLiveListWidgetState } } -class ParseLiveListElementWidget extends StatefulWidget { +class ParseLiveListElementWidget + extends StatefulWidget { const ParseLiveListElementWidget( {Key key, this.stream, @@ -174,8 +173,8 @@ class ParseLiveListElementWidget extends StatefulWidget { @required this.childBuilder}) : super(key: key); - final StreamGetter stream; - final DataGetter loadedData; + final sdk.StreamGetter stream; + final sdk.DataGetter loadedData; final Animation sizeFactor; final Duration duration; final ChildBuilder childBuilder; @@ -186,31 +185,32 @@ class ParseLiveListElementWidget extends StatefulWidget { } } -class _ParseLiveListElementWidgetState +class _ParseLiveListElementWidgetState extends State> with SingleTickerProviderStateMixin { _ParseLiveListElementWidgetState( - DataGetter loadedDataGetter, StreamGetter stream) { - _snapshot = ParseLiveListElementSnapshot(loadedData: loadedDataGetter()); + sdk.DataGetter loadedDataGetter, sdk.StreamGetter stream) { + _snapshot = + sdk.ParseLiveListElementSnapshot(loadedData: loadedDataGetter()); if (stream != null) { _streamSubscription = stream().listen( (T data) { if (widget != null) { setState(() { - _snapshot = ParseLiveListElementSnapshot(loadedData: data); + _snapshot = sdk.ParseLiveListElementSnapshot(loadedData: data); }); } else { - _snapshot = ParseLiveListElementSnapshot(loadedData: data); + _snapshot = sdk.ParseLiveListElementSnapshot(loadedData: data); } }, onError: (Object error) { - if (error is ParseError) { + if (error is sdk.ParseError) { if (widget != null) { setState(() { - _snapshot = ParseLiveListElementSnapshot(error: error); + _snapshot = sdk.ParseLiveListElementSnapshot(error: error); }); } else { - _snapshot = ParseLiveListElementSnapshot(error: error); + _snapshot = sdk.ParseLiveListElementSnapshot(error: error); } } }, @@ -219,7 +219,7 @@ class _ParseLiveListElementWidgetState } } - ParseLiveListElementSnapshot _snapshot; + sdk.ParseLiveListElementSnapshot _snapshot; StreamSubscription _streamSubscription; diff --git a/pubspec.yaml b/packages/flutter/pubspec.yaml similarity index 79% rename from pubspec.yaml rename to packages/flutter/pubspec.yaml index 415a1cee1..fad78ea80 100644 --- a/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -1,4 +1,4 @@ -name: parse_server_sdk +name: parse_server_sdk_flutter description: Flutter plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) version: 1.0.28 homepage: https://github.com/phillwiggins/flutter_parse_sdk @@ -11,22 +11,18 @@ dependencies: flutter: sdk: flutter + parse_server_sdk: + path: ../dart/ + # Networking - web_socket_channel: ^1.1.0 connectivity: ^0.4.9+2 # only used in the flutter part #Database - sembast: ^2.4.7+6 - sembast_web: '>=1.0.0' - xxtea: ^2.0.3 shared_preferences: ^0.5.10 # only used in the flutter part # Utils path_provider: ^1.6.14 # only used in the flutter part - uuid: ^2.2.2 package_info: ^0.4.3 # only used in the flutter part - meta: ^1.1.8 - path: ^1.7.0 dev_dependencies: # Testing diff --git a/test/parse_client_configuration_test.dart b/packages/flutter/test/parse_client_configuration_test.dart similarity index 94% rename from test/parse_client_configuration_test.dart rename to packages/flutter/test/parse_client_configuration_test.dart index 87f9ab35c..f2de6b5cc 100644 --- a/test/parse_client_configuration_test.dart +++ b/packages/flutter/test/parse_client_configuration_test.dart @@ -1,5 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:parse_server_sdk/parse_server_sdk.dart'; +import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() { diff --git a/test/parse_query_test.dart b/packages/flutter/test/parse_query_test.dart similarity index 95% rename from test/parse_query_test.dart rename to packages/flutter/test/parse_query_test.dart index 3a5133fbc..678c11e80 100644 --- a/test/parse_query_test.dart +++ b/packages/flutter/test/parse_query_test.dart @@ -1,6 +1,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; -import 'package:parse_server_sdk/parse_server_sdk.dart'; +import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:shared_preferences/shared_preferences.dart'; class MockClient extends Mock implements ParseHTTPClient {}