// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 library sample_app; import 'dart:async'; import 'package:amplify_datastore/amplify_datastore.dart'; // Uncomment the below line to enable online sync // import 'package:amplify_api/amplify_api.dart'; import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'models/ModelProvider.dart'; part 'event_display_widgets.dart'; part 'queries_display_widgets.dart'; part 'save_model_widgets.dart'; void main() { runApp(MyApp()); } final divider = VerticalDivider( color: Colors.white, width: 10, ); class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { List _posts = []; List _comments = []; List _blogs = []; List _postStreamingData = []; List _blogStreamingData = []; List _commentStreamingData = []; bool _isAmplifyConfigured = false; String _queriesToView = "Post"; //default view Blog? _selectedBlogForNewPost; Post? _selectedPostForNewComment; late Stream> postStream; late Stream> blogStream; late Stream> commentStream; late StreamSubscription hubSubscription; bool _listeningToHub = true; late AmplifyDataStore datastorePlugin; final _titleController = TextEditingController(); final _ratingController = TextEditingController(); final _nameController = TextEditingController(); final _contentController = TextEditingController(); ScrollController _postScrollController = ScrollController(initialScrollOffset: 50.0); ScrollController _blogScrollController = ScrollController(initialScrollOffset: 50.0); ScrollController _commentScrollController = ScrollController(initialScrollOffset: 50.0); @override void initState() { super.initState(); initPlatformState(); } // Platform messages are asynchronous, so we initialize in an async method. Future initPlatformState() async { try { datastorePlugin = AmplifyDataStore( modelProvider: ModelProvider.instance, errorHandler: ((error) => {print("Custom ErrorHandler received: " + error.toString())}), ); await Amplify.addPlugin(datastorePlugin); // Configure // Uncomment the below lines to enable online sync. // await Amplify.addPlugin(AmplifyAPI()); // await Amplify.configure(amplifyconfig); // Remove this line when using the lines above for online sync await Amplify.configure("{}"); } on AmplifyAlreadyConfiguredException { print( 'Amplify was already configured. Looks like app restarted on android.'); } listenToHub(); Amplify.DataStore.observeQuery( Blog.classType, ).listen((QuerySnapshot snapshot) { var count = snapshot.items.length; var now = DateTime.now().toIso8601String(); bool status = snapshot.isSynced; print( '[Observe Query] Blog snapshot received with $count models, status: $status at $now'); setState(() { _blogs = snapshot.items; }); }); Amplify.DataStore.observeQuery( Post.classType, ).listen((QuerySnapshot snapshot) { setState(() { _posts = snapshot.items; }); }); Amplify.DataStore.observeQuery( Comment.classType, ).listen((QuerySnapshot snapshot) { setState(() { _comments = snapshot.items; }); }); // setup streams postStream = Amplify.DataStore.observe(Post.classType); postStream.listen((event) { _postStreamingData.add('Post: ' + (event.eventType.toString() == EventType.delete.toString() ? event.item.id : event.item.title) + ', of type: ' + event.eventType.toString()); }).onError((error) => print(error)); blogStream = Amplify.DataStore.observe(Blog.classType); blogStream.listen((event) { _blogStreamingData.add('Blog: ' + (event.eventType.toString() == EventType.delete.toString() ? event.item.id : event.item.name) + ', of type: ' + event.eventType.toString()); }).onError((error) => print(error)); commentStream = Amplify.DataStore.observe(Comment.classType); commentStream.listen((event) { _commentStreamingData.add('Comment: ' + (event.eventType.toString() == EventType.delete.toString() ? event.item.id : event.item.content) + ', of type: ' + event.eventType.toString()); }).onError((error) => print(error)); setState(() { _isAmplifyConfigured = true; }); } void listenToHub() { setState(() { hubSubscription = Amplify.Hub.listen(HubChannel.DataStore, (msg) { if (msg.type case DataStoreHubEventType.networkStatus) { print('Network status message: $msg'); return; } print(msg); }); _listeningToHub = true; }); } void stopListeningToHub() { hubSubscription.cancel(); setState(() { _listeningToHub = false; }); } savePost(String title, int rating, Blog associatedBlog) async { try { Post post = Post( title: title, rating: rating, created: TemporalDateTime.now(), blog: associatedBlog); await Amplify.DataStore.save(post); } catch (e) { print(e); } } saveBlog(String name) async { try { Blog blog = Blog( name: name, ); await Amplify.DataStore.save(blog); } catch (e) { print(e); } } saveComment(String content, Post associatedPost) async { try { Comment comment = Comment(content: content, post: associatedPost); await Amplify.DataStore.save(comment); } catch (e) { print(e); } } deletePost(String id) async { try { _selectedPostForNewComment = null; await Amplify.DataStore.delete( Post(id: id, title: "", rating: 0, created: TemporalDateTime.now())); } catch (e) { print(e); } } deleteBlog(String id) async { try { _selectedBlogForNewPost = null; await Amplify.DataStore.delete(Blog(id: id, name: "")); } catch (e) { print(e); } } deleteComment(String id) async { try { await Amplify.DataStore.delete(Comment(id: id, content: "")); } catch (e) { print(e); } } void updateQueriesToView(String value) { setState(() { _queriesToView = value; }); } void updateSelectedBlogForNewPost(Blog value) { setState(() { _selectedBlogForNewPost = value; }); } void updateSelectedPostForNewComment(Post value) { setState(() { _selectedPostForNewComment = value; }); } @override Widget build(BuildContext context) { executeAfterBuild(); return MaterialApp( home: Scaffold( appBar: AppBar( centerTitle: true, title: Text( 'Best DataStore App Ever', textAlign: TextAlign.center, ), actions: [ Padding( padding: EdgeInsets.only(right: 20.0), child: GestureDetector( onTap: () async { await Amplify.DataStore.clear(); }, child: Icon( Icons.clear, semanticLabel: "Clear", size: 24.0, ), )), ], ), body: Column( children: [ Padding(padding: EdgeInsets.all(10.0)), // Row for saving blog addBlogWidget(_nameController, _isAmplifyConfigured, saveBlog), // Row for saving post addPostWidget( titleController: _titleController, ratingController: _ratingController, isAmplifyConfigured: _isAmplifyConfigured, allBlogs: _blogs, selectedBlog: _selectedBlogForNewPost, saveFn: savePost, updateSelectedBlogForNewPost: updateSelectedBlogForNewPost, ), // Row for saving comment addCommentWidget( _contentController, _isAmplifyConfigured, _selectedPostForNewComment, _posts, _selectedPostForNewComment, saveComment, updateSelectedPostForNewComment), Padding(padding: EdgeInsets.all(10.0)), // Row for query buttons displayQueryButtons( _isAmplifyConfigured, _queriesToView, updateQueriesToView), Padding(padding: EdgeInsets.all(5.0)), Text("Listen to DataStore Hub"), Switch( value: _listeningToHub, onChanged: (value) { if (_listeningToHub) { stopListeningToHub(); } else { listenToHub(); } }, activeTrackColor: Colors.lightGreenAccent, activeColor: Colors.green, ), Padding(padding: EdgeInsets.all(5.0)), // Showing relevant queries if (_queriesToView == "Post") getWidgetToDisplayPost(_posts, deletePost, _blogs) else if (_queriesToView == "Blog") getWidgetToDisplayBlog(_blogs, deleteBlog) else if (_queriesToView == "Comment") getWidgetToDisplayComment(_comments, deleteComment, _posts), Text(_queriesToView + " Events", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.black, fontSize: 14)), Padding(padding: EdgeInsets.all(5.0)), if (_queriesToView == "Post") getWidgetToDisplayPostEvents( _postScrollController, _postStreamingData, executeAfterBuild) else if (_queriesToView == "Blog") getWidgetToDisplayBlogEvents( _blogScrollController, _blogStreamingData, executeAfterBuild) else if (_queriesToView == "Comment") getWidgetToDisplayCommentEvents(_commentScrollController, _commentStreamingData, executeAfterBuild), ], // replace with any or all query results as needed ), ), ); } @override void didChangeDependencies() { super.didChangeDependencies(); WidgetsBinding.instance.addPostFrameCallback((_) => executeAfterBuild()); } Future executeAfterBuild() async { // this code will get executed after the build method // because of the way async functions are scheduled Future.delayed(const Duration(milliseconds: 500), () { if (_postScrollController.hasClients) _postScrollController.animateTo( _postScrollController.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.easeOut); if (_blogScrollController.hasClients) _blogScrollController.animateTo( _blogScrollController.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.easeOut); if (_commentScrollController.hasClients) _commentScrollController.animateTo( _commentScrollController.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.easeOut); }); } }