From f3b82c6f8cf61f9fda1c8631f01d61c3e26532a2 Mon Sep 17 00:00:00 2001 From: finnp Date: Thu, 13 Apr 2023 21:00:42 +0200 Subject: [PATCH 1/4] go_router --- lib/main.dart | 45 +++++- lib/palette.dart | 1 - lib/services/local_services.dart | 19 ++- lib/services/remote_services.dart | 59 +++---- lib/views/app_bar.dart | 82 +++++----- lib/views/comment_list_view.dart | 18 +-- lib/views/create_post.dart | 49 +++--- lib/views/editPostPage.dart | 35 ++-- lib/views/home_page.dart | 12 -- lib/views/login_page.dart | 39 ++--- lib/views/post_list_view.dart | 7 +- lib/views/post_search_page.dart | 57 ++++--- lib/views/search_page.dart | 8 +- lib/views/splash_page.dart | 3 +- lib/views/updateProfile.dart | 56 +++---- lib/views/user_page.dart | 255 +++++++++++++++--------------- pubspec.lock | 16 ++ pubspec.yaml | 1 + 18 files changed, 412 insertions(+), 350 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 50ddb52..7cb2928 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,54 @@ import 'package:flutter/material.dart'; import 'package:forum/palette.dart'; -import 'package:forum/views/splash_page.dart'; +import 'package:forum/services/local_services.dart'; +import 'package:forum/views/home_page.dart'; +import 'package:forum/views/login_page.dart'; +import 'package:forum/views/post_page.dart'; +import 'package:forum/views/search_page.dart'; +import 'package:forum/views/user_page.dart'; + +import 'package:go_router/go_router.dart'; + +import 'models/post.dart'; void main() => runApp(const MyApp()); +final GoRouter _router = GoRouter( + redirect: (BuildContext context, GoRouterState state) async { + final bool isAuthed = await LocalServices().isAuth(); + if (!isAuthed) { + return '/login'; + } + }, + routes: [ + GoRoute(path: '/login', builder: (context, state) => const LoginPage()), + GoRoute( + path: '/', + builder: (context, state) => const HomePage(), + routes: [ + GoRoute( + name: 'post', + path: 'post/:id', + builder: (context, state) => + FullScreenPostWidget(id: state.params['id']!)), + GoRoute( + name: 'search', + path: 'search', + builder: (context, state) => const SearchPage()), + GoRoute( + name: 'user', + path: 'user/:id', + builder: (context, state) => + UserPage(userId: state.params['id']!)), + ]), + ]); + class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { - return MaterialApp( + return MaterialApp.router( title: 'GHSE Forum', theme: ThemeData( primarySwatch: Palette.BlueToDark, @@ -20,7 +59,7 @@ class MyApp extends StatelessWidget { selectionHandleColor: Palette.OrangeToDark, ), ), - home: const SplashPage(), + routerConfig: _router, ); } } diff --git a/lib/palette.dart b/lib/palette.dart index 3b189ca..e65a3aa 100644 --- a/lib/palette.dart +++ b/lib/palette.dart @@ -113,4 +113,3 @@ class Palette { }, ); } // you can define define int 500 as the default shade and add your lighter tints above and darker tints below. - diff --git a/lib/services/local_services.dart b/lib/services/local_services.dart index 82744f8..2dceec4 100644 --- a/lib/services/local_services.dart +++ b/lib/services/local_services.dart @@ -3,22 +3,29 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; const storage = FlutterSecureStorage(); class LocalServices { - Future getUserName() async{ + Future getUserName() async { return await storage.readAll().then((value) => value['user_name']); } - Future getUserId() async{ + Future getUserId() async { return await storage.readAll().then((value) => value['user_id']); } - Future getToken() async{ + Future getToken() async { return await storage.readAll().then((value) => value['token']); } - Future getExpiration() async{ + Future getExpiration() async { return await storage.readAll().then((value) => value['expiration']); } + Future isAuth() async { + if ((await getToken() != null) && + (DateTime.parse((await getExpiration())!).isAfter(DateTime.now()))) + return true; + return false; + } + Future writeUserData( String userName, String userId, String token, String expiration) async { await storage.write(key: 'user_name', value: userName); @@ -26,9 +33,11 @@ class LocalServices { await storage.write(key: 'token', value: token); await storage.write(key: 'expiration', value: expiration); } + deleteUserData() async { await storage.deleteAll(); } + String getFormatedDate(DateTime date) { String formatedDate = ''; if (date.isAfter(DateTime.now().subtract(const Duration(days: 1)))) { @@ -46,4 +55,4 @@ class LocalServices { } return formatedDate; } -} \ No newline at end of file +} diff --git a/lib/services/remote_services.dart b/lib/services/remote_services.dart index 45ab4ab..80c0d30 100644 --- a/lib/services/remote_services.dart +++ b/lib/services/remote_services.dart @@ -11,7 +11,6 @@ import 'package:forum/services/local_services.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/http_parser.dart'; - final LocalServices localServices = LocalServices(); class RemoteService { @@ -22,7 +21,6 @@ class RemoteService { 'Accept': 'application/json', }; - Future?> getPostsPage(int page) async { var url = Uri.parse('${apiUrl}post/page/$page'); var token = await localServices.getToken(); @@ -38,8 +36,8 @@ class RemoteService { Future register(String userName, String password) async { var url = Uri.parse('${apiUrl}auth/register'); - final body = jsonEncode( - Auth(userName: userName, password: password).toJson()); + final body = + jsonEncode(Auth(userName: userName, password: password).toJson()); final response = await http.post(url, body: body, headers: headers); if (response.statusCode == 200) { @@ -53,8 +51,8 @@ class RemoteService { Future getToken(String userName, String password) async { var url = Uri.parse('${apiUrl}auth/authenticate'); - final body = jsonEncode( - Auth(userName: userName, password: password).toJson()); + final body = + jsonEncode(Auth(userName: userName, password: password).toJson()); headers.remove('Authorization'); final response = await http.post(url, body: body, headers: headers); @@ -75,12 +73,7 @@ class RemoteService { var token = await localServices.getToken(); headers.addAll({'Authorization': 'Bearer $token'}); - final body = jsonEncode( - { - "title": title, - "content": content - } - ); + final body = jsonEncode({"title": title, "content": content}); final http.Response response = await http.post( url, @@ -145,12 +138,8 @@ class RemoteService { var url = Uri.parse('${apiUrl}comment/add/'); var token = await localServices.getToken(); headers.addAll({'Authorization': 'Bearer $token'}); - var response = await http.post(url, headers: headers, body: jsonEncode( - { - "content": text, - "post_id": id - } - )); + var response = await http.post(url, + headers: headers, body: jsonEncode({"content": text, "post_id": id})); if (response.statusCode == 200) { return true; } else { @@ -189,9 +178,10 @@ class RemoteService { var url = Uri.parse('${apiUrl}post/$postId'); var token = await localServices.getToken(); headers.addAll({'Authorization': 'Bearer $token'}); - var response = await http.put( - url, headers: headers, body: jsonEncode(post.toJson())); - if (response.statusCode == 200) {} else { + var response = + await http.put(url, headers: headers, body: jsonEncode(post.toJson())); + if (response.statusCode == 200) { + } else { throw Exception('Failed to update post'); } } @@ -201,7 +191,8 @@ class RemoteService { var token = await localServices.getToken(); headers.addAll({'Authorization': 'Bearer $token'}); var response = await http.delete(url, headers: headers); - if (response.statusCode == 200) {} else { + if (response.statusCode == 200) { + } else { throw Exception('Failed to delete post'); } } @@ -240,12 +231,12 @@ class RemoteService { headers.addAll({'Authorization': 'Bearer $token'}); var response = await http.put(url, headers: headers); - if (response.statusCode == 200) {} else { + if (response.statusCode == 200) { + } else { throw Exception('Failed to update bio'); } } - Future uploadProfileImage(File file) async { var uri = Uri.parse('${apiUrl}file/profile'); var token = await localServices.getToken(); @@ -258,12 +249,12 @@ class RemoteService { request.files.add(await http.MultipartFile.fromPath('file', file.path, contentType: MediaType('image', file.path.split('.').last))); final response = await request.send(); - if(response.statusCode != 200) { + if (response.statusCode != 200) { throw Exception('Failed to upload image'); } } - Future uploadImage(File file,String id) async { + Future uploadImage(File file, String id) async { var uri = Uri.parse('${apiUrl}file/post/$id'); var token = await localServices.getToken(); final headers = { @@ -275,8 +266,22 @@ class RemoteService { request.files.add(await http.MultipartFile.fromPath('file', file.path, contentType: MediaType('image', file.path.split('.').last))); final response = await request.send(); - if(response.statusCode != 200) { + if (response.statusCode != 200) { throw Exception('Failed to upload image'); } } + + Future getPost(String id) async { + print(id); + var url = Uri.parse('${apiUrl}post/$id'); + var token = await localServices.getToken(); + headers.addAll({'Authorization': 'Bearer $token'}); + var response = await http.get(url, headers: headers); + if (response.statusCode == 200) { + var json = response.body; + return Post.fromJson(jsonDecode(json)); + } else { + throw Exception('Failed to load post'); + } + } } diff --git a/lib/views/app_bar.dart b/lib/views/app_bar.dart index 1245be5..921aaf1 100644 --- a/lib/views/app_bar.dart +++ b/lib/views/app_bar.dart @@ -5,6 +5,7 @@ import 'package:forum/services/remote_services.dart'; import 'package:forum/views/editPostPage.dart'; import 'package:forum/views/search_page.dart'; import 'package:forum/views/user_page.dart'; +import 'package:go_router/go_router.dart'; AppBar buildAppBar(BuildContext context) { return AppBar( @@ -49,19 +50,15 @@ AppBar buildMainAppBar(BuildContext context) { ), actions: [ IconButton( - onPressed: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => const SearchPage())), + onPressed: () => context.pushNamed('search'), icon: const Icon(Icons.search), color: Palette.BlueToLight[400]), IconButton( onPressed: () { - localServices.getUserId().then((value) - { - Navigator.of(context) - .push(MaterialPageRoute(builder: (_) => UserPage(userId: value!)) - ); - } - ); + localServices.getUserId().then((value) { + Navigator.of(context).push( + MaterialPageRoute(builder: (_) => UserPage(userId: value!))); + }); }, icon: const Icon(Icons.person), color: Palette.BlueToLight[400], @@ -70,7 +67,6 @@ AppBar buildMainAppBar(BuildContext context) { ); } - AppBar buildEditAppBar(BuildContext context, Post post, Image? image) { return AppBar( leading: IconButton( @@ -100,43 +96,43 @@ AppBar buildEditAppBar(BuildContext context, Post post, Image? image) { color: Palette.BlueToLight[400], onPressed: () { Navigator.of(context).push( - MaterialPageRoute(builder: (_) => EditPostPage(post: post, image: image)), + MaterialPageRoute( + builder: (_) => EditPostPage(post: post, image: image)), ); - }, ), IconButton( - icon: const Icon(Icons.delete), - color: Palette.BlueToLight[400], - onPressed: () { - //Show popup - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text("Delete Post"), - content: const Text("Are you sure you want to delete this post?"), - actions: [ - TextButton( - child: const Text("Cancel"), - onPressed: () { - Navigator.of(context).pop(); - }, - ), - TextButton( - child: const Text("Delete"), - onPressed: () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - RemoteService().deletePost(post.id); - }, - ), - ], - ); - }, - ); - } - ) + icon: const Icon(Icons.delete), + color: Palette.BlueToLight[400], + onPressed: () { + //Show popup + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text("Delete Post"), + content: + const Text("Are you sure you want to delete this post?"), + actions: [ + TextButton( + child: const Text("Cancel"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + TextButton( + child: const Text("Delete"), + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + RemoteService().deletePost(post.id); + }, + ), + ], + ); + }, + ); + }) ], ); } diff --git a/lib/views/comment_list_view.dart b/lib/views/comment_list_view.dart index de8894b..95a5ec4 100644 --- a/lib/views/comment_list_view.dart +++ b/lib/views/comment_list_view.dart @@ -35,14 +35,14 @@ class CommentWidget extends StatelessWidget { const SizedBox(width: 4), GestureDetector( onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserPage( - userId: comment.userId, - ), - ), - ); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UserPage( + userId: comment.userId, + ), + ), + ); }, child: Text( comment.userName, @@ -80,4 +80,4 @@ class CommentWidget extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/create_post.dart b/lib/views/create_post.dart index 96a76d7..d7522f2 100644 --- a/lib/views/create_post.dart +++ b/lib/views/create_post.dart @@ -21,11 +21,11 @@ class AddPostWidgetState extends State { Future pickImage() async { try { final image = await ImagePicker().pickImage(source: ImageSource.gallery); - if(image == null) return; + if (image == null) return; final imageTemp = File(image.path); setState(() => this.image = imageTemp); - } on PlatformException catch(e) { - rethrow; + } on PlatformException catch (e) { + rethrow; } } @@ -101,16 +101,16 @@ class AddPostWidgetState extends State { const SizedBox(height: 16), if (image != null) Padding( - padding: const EdgeInsets.only(top: 16), - child: Image.file(image!), - ), - ElevatedButton.icon( - onPressed: () { - pickImage(); - }, - label: const Text('Upload Picture'), - icon: const Icon(Icons.image), + padding: const EdgeInsets.only(top: 16), + child: Image.file(image!), ), + ElevatedButton.icon( + onPressed: () { + pickImage(); + }, + label: const Text('Upload Picture'), + icon: const Icon(Icons.image), + ), const SizedBox(height: 16), ElevatedButton.icon( onPressed: () async { @@ -121,18 +121,21 @@ class AddPostWidgetState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: - Text('Please fill in all fields'))); + Text('Please fill in all fields'))); return; } - remoteService.addPost( - title: titel, - content: content, - ).then((value) => { - if (image != null) { - remoteService.uploadImage( - image!, value.id) - } - }); + remoteService + .addPost( + title: titel, + content: content, + ) + .then((value) => { + if (image != null) + { + remoteService.uploadImage( + image!, value.id) + } + }); Navigator.of(context).pop(); }, icon: const Icon(Icons.send), @@ -146,4 +149,4 @@ class AddPostWidgetState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/editPostPage.dart b/lib/views/editPostPage.dart index 2844b7c..7cedf67 100644 --- a/lib/views/editPostPage.dart +++ b/lib/views/editPostPage.dart @@ -10,7 +10,8 @@ import 'package:forum/views/post_page.dart'; import 'package:image_picker/image_picker.dart'; class EditPostPage extends StatefulWidget { - const EditPostPage({Key? key, required this.post, required this.image}) : super(key: key); + const EditPostPage({Key? key, required this.post, required this.image}) + : super(key: key); final Post post; final Image? image; @@ -18,8 +19,6 @@ class EditPostPage extends StatefulWidget { editPostPageState createState() => editPostPageState(); } - - class editPostPageState extends State { final titleController = TextEditingController(); final contentController = TextEditingController(); @@ -28,13 +27,14 @@ class editPostPageState extends State { Future pickImage() async { try { final image = await ImagePicker().pickImage(source: ImageSource.gallery); - if(image == null) return; + if (image == null) return; final imageTemp = File(image.path); setState(() => this.image = imageTemp); - } on PlatformException catch(e) { - rethrow; + } on PlatformException catch (e) { + rethrow; } } + @override void initState() { super.initState(); @@ -112,7 +112,11 @@ class editPostPageState extends State { style: const TextStyle(color: Colors.white), ), const SizedBox(height: 16), - if (image != null)Padding(padding: const EdgeInsets.only(top: 16), child: Image.file(image!),) + if (image != null) + Padding( + padding: const EdgeInsets.only(top: 16), + child: Image.file(image!), + ) else if (prevImage != null) Padding( padding: const EdgeInsets.only(top: 16), @@ -134,17 +138,24 @@ class editPostPageState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: - Text('Please fill in all fields'))); + Text('Please fill in all fields'))); return; } widget.post.title = titel; widget.post.content = content; - RemoteService().updatePost(widget.post.id,widget.post); - if(image != null) RemoteService().uploadImage(image!,widget.post.id); + RemoteService() + .updatePost(widget.post.id, widget.post); + if (image != null) + RemoteService().uploadImage(image!, widget.post.id); //return to home page Navigator.of(context).pop(); //reload post page with new post - Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => FullScreenPostWidget(post: widget.post,))); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => FullScreenPostWidget( + post: widget.post, + ))); }, icon: const Icon(Icons.post_add), label: const Text('Update Post')), @@ -157,4 +168,4 @@ class editPostPageState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/home_page.dart b/lib/views/home_page.dart index bbdc31d..16ff6d1 100644 --- a/lib/views/home_page.dart +++ b/lib/views/home_page.dart @@ -7,7 +7,6 @@ import 'package:forum/views/app_bar.dart'; import 'package:forum/views/create_post.dart'; import 'package:forum/views/post_list_view.dart'; - RemoteService remoteService = RemoteService(); class HomePage extends StatefulWidget { @@ -99,14 +98,3 @@ class HomePageState extends State { ); } } - - - - - - - - - - - diff --git a/lib/views/login_page.dart b/lib/views/login_page.dart index d20668b..77a75b1 100644 --- a/lib/views/login_page.dart +++ b/lib/views/login_page.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:forum/palette.dart'; import 'package:forum/views/home_page.dart'; +import 'package:go_router/go_router.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override LoginPageState createState() => LoginPageState(); - } class LoginPageState extends State { @@ -30,7 +30,6 @@ class LoginPageState extends State { @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Row( @@ -89,11 +88,11 @@ class LoginPageState extends State { ), style: const TextStyle(color: Colors.white), ), - const SizedBox(height: 16), + const SizedBox(height: 16), TextField( obscureText: _obscureText, controller: passwordController, - decoration: InputDecoration( + decoration: InputDecoration( labelText: 'Password', labelStyle: const TextStyle( color: Palette.OrangeToDark, fontSize: 20), @@ -104,24 +103,21 @@ class LoginPageState extends State { borderSide: BorderSide(color: Palette.OrangeToDark), ), suffixIcon: IconButton( - icon: Icon( - _obscureText - ? Icons.visibility - : Icons.visibility_off, - color: Palette.OrangeToDark, - ), - onPressed: () { - setState(() { - _obscureText = !_obscureText; - }); - }, - ), - + icon: Icon( + _obscureText + ? Icons.visibility + : Icons.visibility_off, + color: Palette.OrangeToDark, + ), + onPressed: () { + setState(() { + _obscureText = !_obscureText; + }); + }, ), + ), style: const TextStyle(color: Colors.white), ), - - const SizedBox(height: 16), ElevatedButton.icon( onPressed: () async { @@ -132,10 +128,7 @@ class LoginPageState extends State { await remoteService .getToken(userController.text, passwordController.text) - .then((value) => Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => const HomePage()))); + .then((value) => context.go('/home')); } }, icon: const Icon(Icons.login), diff --git a/lib/views/post_list_view.dart b/lib/views/post_list_view.dart index 2b94229..42ba570 100644 --- a/lib/views/post_list_view.dart +++ b/lib/views/post_list_view.dart @@ -3,6 +3,7 @@ import 'package:forum/models/post.dart'; import 'package:forum/palette.dart'; import 'package:forum/services/local_services.dart'; import 'package:forum/views/post_page.dart'; +import 'package:go_router/go_router.dart'; class PostWidget extends StatelessWidget { final Post post; @@ -22,7 +23,7 @@ class PostWidget extends StatelessWidget { ), child: ListTile( contentPadding: - const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + const EdgeInsets.symmetric(vertical: 8, horizontal: 16), title: Text( post.title, maxLines: 2, @@ -50,7 +51,7 @@ class PostWidget extends StatelessWidget { ], ), trailing: - const Icon(Icons.arrow_forward_ios, color: Palette.BlueToDark), + const Icon(Icons.arrow_forward_ios, color: Palette.BlueToDark), onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (_) => FullScreenPostWidget( @@ -62,4 +63,4 @@ class PostWidget extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/post_search_page.dart b/lib/views/post_search_page.dart index e33c0ed..65601f7 100644 --- a/lib/views/post_search_page.dart +++ b/lib/views/post_search_page.dart @@ -23,7 +23,6 @@ class PostPageState extends State { int page = 0; bool end = false; - @override void initState() { super.initState(); @@ -31,7 +30,7 @@ class PostPageState extends State { } getData() async { - posts = await remoteService.search(widget.search,0); + posts = await remoteService.search(widget.search, 0); if (posts != null) { setState(() { isLoaded = true; @@ -41,10 +40,10 @@ class PostPageState extends State { addNextPage() { page++; - remoteService.search(widget.search,page).then((value) => setState(() { - posts!.addAll(value!); - if (value.isEmpty) end = true; - })); + remoteService.search(widget.search, page).then((value) => setState(() { + posts!.addAll(value!); + if (value.isEmpty) end = true; + })); } @override @@ -74,34 +73,34 @@ class PostPageState extends State { ), Expanded( child: Visibility( - visible: isLoaded, - replacement: const Center( - child: CircularProgressIndicator(), - ), - child: ListView.builder( - itemBuilder: (context, index) { - int? postLength = posts?.length; + visible: isLoaded, + replacement: const Center( + child: CircularProgressIndicator(), + ), + child: ListView.builder( + itemBuilder: (context, index) { + int? postLength = posts?.length; - if (index == postLength && !end) { - addNextPage(); - return Container( - padding: const EdgeInsets.all(16), - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } else if (index < postLength!) { - final post = posts![index]; - return PostWidget(post: post); - } - return null; - }, + if (index == postLength && !end) { + addNextPage(); + return Container( + padding: const EdgeInsets.all(16), + child: const Center( + child: CircularProgressIndicator(), + ), + ); + } else if (index < postLength!) { + final post = posts![index]; + return PostWidget(post: post); + } + return null; + }, + ), ), ), - ), ], ), ), ); } -} \ No newline at end of file +} diff --git a/lib/views/search_page.dart b/lib/views/search_page.dart index 38ed5c4..7c7548a 100644 --- a/lib/views/search_page.dart +++ b/lib/views/search_page.dart @@ -73,14 +73,12 @@ class SearchPageState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: - Text('Please fill in all fields'))); + Text('Please fill in all fields'))); return; } - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => - PostPage(search: search))); + builder: (context) => PostPage(search: search))); }, icon: const Icon(Icons.search), label: const Text('Search')), @@ -93,4 +91,4 @@ class SearchPageState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/splash_page.dart b/lib/views/splash_page.dart index 9fcab10..3f6be64 100644 --- a/lib/views/splash_page.dart +++ b/lib/views/splash_page.dart @@ -55,7 +55,8 @@ class SplashPageState extends State { onPressed: () { Navigator.pushReplacement( context, - MaterialPageRoute(builder: (context) => const LoginPage()), + MaterialPageRoute( + builder: (context) => const LoginPage()), ); }, child: const Text('Log In'), diff --git a/lib/views/updateProfile.dart b/lib/views/updateProfile.dart index 3aee1d5..547ff65 100644 --- a/lib/views/updateProfile.dart +++ b/lib/views/updateProfile.dart @@ -1,5 +1,3 @@ - - import 'dart:io'; import 'package:flutter/cupertino.dart'; @@ -14,25 +12,28 @@ import 'package:image_picker/image_picker.dart'; class UpdateProfileWidget extends StatefulWidget { final Image profilePicture; final String bio; - const UpdateProfileWidget({Key? key, required this.profilePicture, required this.bio}) : super(key: key); + const UpdateProfileWidget( + {Key? key, required this.profilePicture, required this.bio}) + : super(key: key); @override UpdateProfileWidgetState createState() => UpdateProfileWidgetState(); } -class UpdateProfileWidgetState extends State{ +class UpdateProfileWidgetState extends State { final controller = TextEditingController(); File? image; Image? profilePicture; Future pickImage() async { try { final image = await ImagePicker().pickImage(source: ImageSource.gallery); - if(image == null) return; + if (image == null) return; final imageTemp = File(image.path); setState(() => this.image = imageTemp); - } on PlatformException catch(e) { + } on PlatformException catch (e) { print('Failed to pick image: $e'); } } + @override void initState() { super.initState(); @@ -66,18 +67,18 @@ class UpdateProfileWidgetState extends State{ ), ), const SizedBox(height: 10), - if(image != null) + if (image != null) CircleAvatar( radius: 90, backgroundImage: FileImage(image!), ) - else - Hero(tag: profilePicture!, child: - CircleAvatar( - radius: 90, - backgroundImage: profilePicture?.image, - )) - , + else + Hero( + tag: profilePicture!, + child: CircleAvatar( + radius: 90, + backgroundImage: profilePicture?.image, + )), ElevatedButton.icon( onPressed: () { pickImage(); @@ -91,8 +92,8 @@ class UpdateProfileWidgetState extends State{ controller: controller, decoration: const InputDecoration( labelText: 'Bio', - labelStyle: TextStyle( - color: Palette.OrangeToDark, fontSize: 20), + labelStyle: + TextStyle(color: Palette.OrangeToDark, fontSize: 20), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Palette.OrangeToDark), ), @@ -100,22 +101,18 @@ class UpdateProfileWidgetState extends State{ borderSide: BorderSide(color: Palette.OrangeToDark), ), ), - ), - const SizedBox(height:10), + const SizedBox(height: 10), ElevatedButton.icon( label: const Text('Confirm'), icon: const Icon(Icons.save), - onPressed: () { - if (image != null) remoteService.uploadProfileImage(image!); - remoteService.updateBio(controller.text).then((value) { - localServices.getUserId().then((value) - { - Navigator.pop(context); - } - ); - } - ); + onPressed: () { + if (image != null) remoteService.uploadProfileImage(image!); + remoteService.updateBio(controller.text).then((value) { + localServices.getUserId().then((value) { + Navigator.pop(context); + }); + }); }, ), ], @@ -123,5 +120,4 @@ class UpdateProfileWidgetState extends State{ ), ); } - -} \ No newline at end of file +} diff --git a/lib/views/user_page.dart b/lib/views/user_page.dart index a460800..33ec686 100644 --- a/lib/views/user_page.dart +++ b/lib/views/user_page.dart @@ -8,6 +8,7 @@ import 'package:forum/views/app_bar.dart'; import 'package:forum/views/home_page.dart'; import 'package:forum/views/post_list_view.dart'; import 'package:forum/views/updateProfile.dart'; +import 'package:go_router/go_router.dart'; import 'login_page.dart'; @@ -25,7 +26,6 @@ class UserPageState extends State { UserResponse? user; bool isUsersPage = false; - String bio = ''; late Image profilePicture = Image.asset('assets/images/def1.png'); @@ -43,148 +43,155 @@ class UserPageState extends State { profilePicture = Image.memory(value); }); }); - RemoteService().getUserByUUID(userId).then((value) => - setState(() { - user = value; - bio = value.bio ?? ""; - _isLoading = false; - })).then((value) => LocalServices().getUserId().then((value) { - if (value == userId) { - setState(() { - isUsersPage = true; - }); - } - })); + RemoteService() + .getUserByUUID(userId) + .then((value) => setState(() { + user = value; + bio = value.bio ?? ""; + _isLoading = false; + })) + .then((value) => LocalServices().getUserId().then((value) { + if (value == userId) { + setState(() { + isUsersPage = true; + }); + } + })); - RemoteService().getPostsOfUser(0,userId).then((value) { - posts = value; - setState(() { - _isLoadingPosts = false; - }); + RemoteService().getPostsOfUser(0, userId).then((value) { + posts = value; + setState(() { + _isLoadingPosts = false; }); + }); } addNextPage() { page++; - remoteService.getPostsOfUser(page,userId).then((value) => setState(() { - posts!.addAll(value); - if (value.isEmpty) end = true; - })); + remoteService.getPostsOfUser(page, userId).then((value) => setState(() { + posts!.addAll(value); + if (value.isEmpty) end = true; + })); } @override Widget build(BuildContext context) { return Scaffold( - appBar: isUsersPage ? AppBar( - leading: IconButton( - color: Palette.OrangeToLight, - icon: const Icon(Icons.arrow_back_ios), - onPressed: () { - Navigator.pop(context); - }, - ), - titleSpacing: 0, - title: Row( - children: [ - Image.asset( - 'assets/images/ghse_logo.png', - fit: BoxFit.contain, - height: 32, - ), - const Text( - ' Forum', - style: TextStyle(color: Palette.OrangeToLight), - ), - ], - ), - actions: [ - IconButton( - icon: const Icon(Icons.logout), - color: Palette.BlueToLight[400], - onPressed: () { - LocalServices().deleteUserData(); - Navigator.pop(context); - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => const LoginPage()), - ); - }, - ), - IconButton( - icon: const Icon(Icons.edit), - color: Palette.BlueToLight[400], - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => UpdateProfileWidget(profilePicture: profilePicture, bio: bio,)), - ); - }, - ), - ], - ) : buildAppBar(context), - body: _isLoading - ? const Center(child: CircularProgressIndicator()) - : Container( + appBar: isUsersPage + ? AppBar( + leading: IconButton( + color: Palette.OrangeToLight, + icon: const Icon(Icons.arrow_back_ios), + onPressed: () { + Navigator.pop(context); + }, + ), + titleSpacing: 0, + title: Row( + children: [ + Image.asset( + 'assets/images/ghse_logo.png', + fit: BoxFit.contain, + height: 32, + ), + const Text( + ' Forum', + style: TextStyle(color: Palette.OrangeToLight), + ), + ], + ), + actions: [ + IconButton( + icon: const Icon(Icons.logout), + color: Palette.BlueToLight[400], + onPressed: () { + LocalServices().deleteUserData(); + context.go('/login'); + }, + ), + IconButton( + icon: const Icon(Icons.edit), + color: Palette.BlueToLight[400], + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => UpdateProfileWidget( + profilePicture: profilePicture, + bio: bio, + )), + ); + }, + ), + ], + ) + : buildAppBar(context), + body: Container( color: Palette.BlueToDark, child: Padding( padding: const EdgeInsets.all(16), child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Hero(tag: profilePicture, child: - CircleAvatar( - radius: 90, - backgroundImage: profilePicture.image, - )), - const SizedBox(height: 16), - Text( - user?.userName ?? "", - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white), - ), - const SizedBox(height: 16), - Text( - bio, - textAlign: TextAlign.center, - style: - const TextStyle(fontSize: 16, color: Colors.white), - ), - const SizedBox(height: 16), - Expanded( - child: Visibility( - visible: !_isLoadingPosts, - replacement: const Center( - child: CircularProgressIndicator(), - ), - child: ListView.builder( - itemBuilder: (context, index) { - int? postLength = posts?.length; + child: _isLoading + ? const Center( + child: CircularProgressIndicator( + color: Palette.OrangeToDark, + )) + : Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Hero( + tag: profilePicture, + child: CircleAvatar( + radius: 90, + backgroundImage: profilePicture.image, + )), + const SizedBox(height: 16), + Text( + user?.userName ?? "", + style: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Colors.white), + ), + const SizedBox(height: 16), + Text( + bio, + textAlign: TextAlign.center, + style: + const TextStyle(fontSize: 16, color: Colors.white), + ), + const SizedBox(height: 16), + Expanded( + child: Visibility( + visible: !_isLoadingPosts, + replacement: const Center( + child: CircularProgressIndicator(), + ), + child: ListView.builder( + itemBuilder: (context, index) { + int? postLength = posts?.length; - if (index == postLength && !end) { - addNextPage(); - return Container( - padding: const EdgeInsets.all(16), - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } else if (index < postLength!) { - final post = posts![index]; - return PostWidget(post: post); - } - return null; - }, - ), + if (index == postLength && !end) { + addNextPage(); + return Container( + padding: const EdgeInsets.all(16), + child: const Center( + child: CircularProgressIndicator(), + ), + ); + } else if (index < postLength!) { + final post = posts![index]; + return PostWidget(post: post); + } + return null; + }, + ), + ), + ), + ], ), - ), - ], - ), ), ), ), ); } - -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 248c98a..6c4146c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -216,6 +216,14 @@ packages: description: flutter source: sdk version: "0.0.0" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: "50bc08b72ede07daaf897deb4c00ca8fd8976c8821a4c84d7aeef0e92d1c5620" + url: "https://pub.dev" + source: hosted + version: "6.5.6" http: dependency: "direct main" description: @@ -304,6 +312,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d" + url: "https://pub.dev" + source: hosted + version: "1.1.1" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9be2bb4..47cbe95 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: http_parser: ^4.0.2 share_plus: ^6.3.2 path_provider: ^2.0.14 + go_router: ^6.5.6 From 80dd68f89c1027b5428f1dcbcb655caea8dc1f2a Mon Sep 17 00:00:00 2001 From: finnp Date: Thu, 13 Apr 2023 21:05:38 +0200 Subject: [PATCH 2/4] fixes --- lib/services/local_services.dart | 19 +- lib/services/remote_services.dart | 1 - lib/views/app_bar.dart | 1 - lib/views/comment_list_view.dart | 2 +- lib/views/create_post.dart | 11 +- lib/views/editPostPage.dart | 18 +- lib/views/post_list_view.dart | 3 +- lib/views/post_page.dart | 364 +++++++++++++++--------------- lib/views/updateProfile.dart | 6 +- lib/views/user_page.dart | 1 - 10 files changed, 219 insertions(+), 207 deletions(-) diff --git a/lib/services/local_services.dart b/lib/services/local_services.dart index 2dceec4..faa79d2 100644 --- a/lib/services/local_services.dart +++ b/lib/services/local_services.dart @@ -21,8 +21,9 @@ class LocalServices { Future isAuth() async { if ((await getToken() != null) && - (DateTime.parse((await getExpiration())!).isAfter(DateTime.now()))) + (DateTime.parse((await getExpiration())!).isAfter(DateTime.now()))) { return true; + } return false; } @@ -38,21 +39,21 @@ class LocalServices { await storage.deleteAll(); } - String getFormatedDate(DateTime date) { - String formatedDate = ''; + String getFormattedDate(DateTime date) { + String formattedDate = ''; if (date.isAfter(DateTime.now().subtract(const Duration(days: 1)))) { - formatedDate = '${date.hour}:'; + formattedDate = '${date.hour}:'; if (date.hour < 10) { - formatedDate = '0$formatedDate'; + formattedDate = '0$formattedDate'; } if (date.minute < 10) { - formatedDate = '${formatedDate}0${date.minute}'; + formattedDate = '${formattedDate}0${date.minute}'; } else { - formatedDate = '$formatedDate${date.minute}'; + formattedDate = '$formattedDate${date.minute}'; } } else { - formatedDate = '${date.day}/${date.month}/${date.year}'; + formattedDate = '${date.day}/${date.month}/${date.year}'; } - return formatedDate; + return formattedDate; } } diff --git a/lib/services/remote_services.dart b/lib/services/remote_services.dart index 80c0d30..3360bb8 100644 --- a/lib/services/remote_services.dart +++ b/lib/services/remote_services.dart @@ -272,7 +272,6 @@ class RemoteService { } Future getPost(String id) async { - print(id); var url = Uri.parse('${apiUrl}post/$id'); var token = await localServices.getToken(); headers.addAll({'Authorization': 'Bearer $token'}); diff --git a/lib/views/app_bar.dart b/lib/views/app_bar.dart index 921aaf1..1537a91 100644 --- a/lib/views/app_bar.dart +++ b/lib/views/app_bar.dart @@ -3,7 +3,6 @@ import 'package:forum/models/post.dart'; import 'package:forum/palette.dart'; import 'package:forum/services/remote_services.dart'; import 'package:forum/views/editPostPage.dart'; -import 'package:forum/views/search_page.dart'; import 'package:forum/views/user_page.dart'; import 'package:go_router/go_router.dart'; diff --git a/lib/views/comment_list_view.dart b/lib/views/comment_list_view.dart index 95a5ec4..c4a10b7 100644 --- a/lib/views/comment_list_view.dart +++ b/lib/views/comment_list_view.dart @@ -14,7 +14,7 @@ class CommentWidget extends StatelessWidget { @override Widget build(BuildContext context) { - String date = LocalServices().getFormatedDate(comment.date); + String date = LocalServices().getFormattedDate(comment.date); return Container( decoration: BoxDecoration( color: Palette.BlueToLight[400], diff --git a/lib/views/create_post.dart b/lib/views/create_post.dart index d7522f2..a7e88df 100644 --- a/lib/views/create_post.dart +++ b/lib/views/create_post.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:forum/palette.dart'; @@ -25,7 +26,9 @@ class AddPostWidgetState extends State { final imageTemp = File(image.path); setState(() => this.image = imageTemp); } on PlatformException catch (e) { - rethrow; + if (kDebugMode) { + print('Failed to pick image: $e'); + } } } @@ -114,9 +117,9 @@ class AddPostWidgetState extends State { const SizedBox(height: 16), ElevatedButton.icon( onPressed: () async { - var titel = titleController.text; + var title = titleController.text; var content = contentController.text; - if (titel.isEmpty | content.isEmpty) { + if (title.isEmpty | content.isEmpty) { //show error message ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -126,7 +129,7 @@ class AddPostWidgetState extends State { } remoteService .addPost( - title: titel, + title: title, content: content, ) .then((value) => { diff --git a/lib/views/editPostPage.dart b/lib/views/editPostPage.dart index 7cedf67..901902b 100644 --- a/lib/views/editPostPage.dart +++ b/lib/views/editPostPage.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:forum/models/post.dart'; @@ -16,10 +17,10 @@ class EditPostPage extends StatefulWidget { final Image? image; @override - editPostPageState createState() => editPostPageState(); + EditPostPageState createState() => EditPostPageState(); } -class editPostPageState extends State { +class EditPostPageState extends State { final titleController = TextEditingController(); final contentController = TextEditingController(); File? image; @@ -31,7 +32,9 @@ class editPostPageState extends State { final imageTemp = File(image.path); setState(() => this.image = imageTemp); } on PlatformException catch (e) { - rethrow; + if (kDebugMode) { + print('Failed to pick image: $e'); + } } } @@ -131,9 +134,9 @@ class editPostPageState extends State { ), ElevatedButton.icon( onPressed: () async { - var titel = titleController.text; + var title = titleController.text; var content = contentController.text; - if (titel.isEmpty | content.isEmpty) { + if (title.isEmpty | content.isEmpty) { //show error message ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -141,12 +144,13 @@ class editPostPageState extends State { Text('Please fill in all fields'))); return; } - widget.post.title = titel; + widget.post.title = title; widget.post.content = content; RemoteService() .updatePost(widget.post.id, widget.post); - if (image != null) + if (image != null) { RemoteService().uploadImage(image!, widget.post.id); + } //return to home page Navigator.of(context).pop(); //reload post page with new post diff --git a/lib/views/post_list_view.dart b/lib/views/post_list_view.dart index 42ba570..ae96b49 100644 --- a/lib/views/post_list_view.dart +++ b/lib/views/post_list_view.dart @@ -3,7 +3,6 @@ import 'package:forum/models/post.dart'; import 'package:forum/palette.dart'; import 'package:forum/services/local_services.dart'; import 'package:forum/views/post_page.dart'; -import 'package:go_router/go_router.dart'; class PostWidget extends StatelessWidget { final Post post; @@ -12,7 +11,7 @@ class PostWidget extends StatelessWidget { @override Widget build(BuildContext context) { - String date = LocalServices().getFormatedDate(post.date); + String date = LocalServices().getFormattedDate(post.date); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), diff --git a/lib/views/post_page.dart b/lib/views/post_page.dart index 4fb09db..ded93ca 100644 --- a/lib/views/post_page.dart +++ b/lib/views/post_page.dart @@ -9,14 +9,14 @@ import 'package:forum/services/remote_services.dart'; import 'package:forum/views/app_bar.dart'; import 'package:forum/views/comment_list_view.dart'; import 'package:forum/views/home_page.dart'; -import 'package:forum/views/user_page.dart'; +import 'package:go_router/go_router.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; class FullScreenPostWidget extends StatefulWidget { - final Post post; - - const FullScreenPostWidget({Key? key, required this.post}) : super(key: key); + final Post? post; + final String? id; + const FullScreenPostWidget({Key? key, this.post, this.id}) : super(key: key); @override FullScreenPostWidgetState createState() => FullScreenPostWidgetState(); @@ -32,6 +32,7 @@ class FullScreenPostWidgetState extends State { bool isOwnPost = false; Image? image; Uint8List? imageBytes; + String date = ''; @override void dispose() { @@ -43,8 +44,21 @@ class FullScreenPostWidgetState extends State { @override void initState() { super.initState(); - post = widget.post; - getData(); + if (widget.post != null) { + isLoaded = true; + post = widget.post!; + date = LocalServices().getFormattedDate(post.date); + getData(); + } else { + RemoteService().getPost(widget.id!).then((value) { + setState(() { + post = value; + date = LocalServices().getFormattedDate(post.date); + isLoaded = true; + getData(); + }); + }); + } } getData() async { @@ -55,9 +69,6 @@ class FullScreenPostWidgetState extends State { }); }); comments = await remoteService.getComments(0, post.id); - setState(() { - isLoaded = true; - }); LocalServices().getUserId().then((value) { if (value == post.userId) { setState(() { @@ -79,8 +90,6 @@ class FullScreenPostWidgetState extends State { //Page to display a post in full screen including the post Content and Comments @override Widget build(BuildContext context) { - String date = LocalServices().getFormatedDate(post.date); - return Scaffold( backgroundColor: Palette.BlueToDark, appBar: isOwnPost @@ -90,183 +99,180 @@ class FullScreenPostWidgetState extends State { physics: const BouncingScrollPhysics(), child: Container( color: Palette.BlueToDark, - child: Column( - children: [ - Container( - decoration: BoxDecoration( - color: Palette.BlueToLight[400], - borderRadius: BorderRadius.circular(8), - ), - margin: const EdgeInsets.only( - top: 16, bottom: 16, left: 16, right: 16), - padding: const EdgeInsets.only( - top: 16, bottom: 16, left: 16, right: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: !isLoaded + ? const CircularProgressIndicator() + : Column( children: [ - Row( - children: [ - const Icon( - Icons.account_circle, - size: 15, - color: Colors.black54, - ), - const SizedBox(width: 4), - GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserPage( - userId: post.userId, + Container( + decoration: BoxDecoration( + color: Palette.BlueToLight[400], + borderRadius: BorderRadius.circular(8), + ), + margin: const EdgeInsets.only( + top: 16, bottom: 16, left: 16, right: 16), + padding: const EdgeInsets.only( + top: 16, bottom: 16, left: 16, right: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon( + Icons.account_circle, + size: 15, + color: Colors.black54, + ), + const SizedBox(width: 4), + GestureDetector( + onTap: () { + context.pushNamed('user', + params: {'id': post.userId}); + }, + child: Text( + post.userName, + style: const TextStyle( + fontSize: 16, + color: Colors.black54, + ), ), ), - ); - }, - child: Text( - post.userName, + const SizedBox(width: 16), + const Icon( + Icons.access_time, + size: 15, + color: Colors.black54, + ), + const SizedBox(width: 4), + Text( + date, + style: const TextStyle( + fontSize: 16, + color: Colors.black54, + ), + ), + const SizedBox(width: 16), + if (post.edited) + const Text( + '(Edited)', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ], + ), + const SizedBox(height: 10), + Text( + post.title, style: const TextStyle( - fontSize: 16, - color: Colors.black54, + fontSize: 24, + fontWeight: FontWeight.bold, ), ), - ), - const SizedBox(width: 16), - const Icon( - Icons.access_time, - size: 15, - color: Colors.black54, - ), - const SizedBox(width: 4), - Text( - date, - style: const TextStyle( - fontSize: 16, - color: Colors.black54, - ), - ), - const SizedBox(width: 16), - if (post.edited) - const Text( - '(Edited)', - style: TextStyle(fontStyle: FontStyle.italic), + const SizedBox(height: 6), + Text( + post.content, + style: const TextStyle( + fontSize: 16, + ), ), - ], - ), - const SizedBox(height: 10), - Text( - post.title, - style: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 6), - Text( - post.content, - style: const TextStyle( - fontSize: 16, + // Picture here if it exists + if (image != null) + Padding( + padding: const EdgeInsets.only(top: 16), + child: GestureDetector( + onTap: () async { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + FullScreenImage(image: imageBytes!), + ), + ); + }, + child: Hero( + tag: 'imageHero', + child: image!, + ), + ), + ), + ], ), ), - // Picture here if it exists - if (image != null) - Padding( - padding: const EdgeInsets.only(top: 16), - child: GestureDetector( - onTap: () async { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => FullScreenImage(image: imageBytes!), - ), - ); - }, - child: Hero( - tag: 'imageHero', - child: image!, + Container( + color: Palette.BlueToLight, + padding: const EdgeInsets.only( + top: 16, bottom: 16, left: 16, right: 16), + child: Row( + children: const [ + Text( + 'Comments', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + color: Palette.OrangeToDark, + ), ), + ], ), ), - ], - ), - ), - Container( - color: Palette.BlueToLight, - padding: const EdgeInsets.only( - top: 16, bottom: 16, left: 16, right: 16), - child: Row( - children: const [ - Text( - 'Comments', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Palette.OrangeToDark, + Padding( + padding: const EdgeInsets.only( + top: 0, bottom: 16, left: 16, right: 16), + child: TextField( + cursorColor: Colors.black, + controller: commentController, + decoration: InputDecoration( + suffixIcon: IconButton( + onPressed: () { + String text = commentController.text; + if (commentController.text.isNotEmpty) { + remoteService + .addComment(post.id, text) + .then((value) => { + if (value == true) + { + setState(() { + comments.insert( + 0, + Comment( + userId: '0', + content: text, + date: DateTime.now(), + userName: 'Me', + id: '0')); + }) + } + }); + commentController.clear(); + } + }, + icon: const Icon(Icons.send)), + fillColor: Palette.Back, + filled: true, + hintText: 'Write a comment', + ), + style: const TextStyle(color: Colors.black), ), ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 0, bottom: 16, left: 16, right: 16), - child: TextField( - cursorColor: Colors.black, - controller: commentController, - decoration: InputDecoration( - suffixIcon: IconButton( - onPressed: () { - String text = commentController.text; - if (commentController.text.isNotEmpty) { - remoteService - .addComment(post.id, text) - .then((value) => { - if (value == true) - { - setState(() { - comments.insert( - 0, - Comment( - userId: '0', - content: text, - date: DateTime.now(), - userName: 'Me', - id: '0')); - }) - } - }); - commentController.clear(); + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + int? postLength = comments.length; + if (index == postLength && !end) { + addNextPage(); + return Container( + padding: const EdgeInsets.all(16), + child: const Center( + child: CircularProgressIndicator(), + ), + ); + } else if (index < postLength) { + final comment = comments[index]; + return CommentWidget(comment: comment); } - }, - icon: const Icon(Icons.send)), - fillColor: Palette.Back, - filled: true, - hintText: 'Write a comment', - ), - style: const TextStyle(color: Colors.black), + return null; + }), + ], ), - ), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - int? postLength = comments.length; - if (index == postLength && !end) { - addNextPage(); - return Container( - padding: const EdgeInsets.all(16), - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } else if (index < postLength) { - final comment = comments[index]; - return CommentWidget(comment: comment); - } - return null; - }), - ], - ), ), ), ); @@ -274,7 +280,7 @@ class FullScreenPostWidgetState extends State { } class FullScreenImage extends StatelessWidget { - final Uint8List image; + final Uint8List image; const FullScreenImage({super.key, required this.image}); @override @@ -293,15 +299,15 @@ class FullScreenImage extends StatelessWidget { constrained: true, child: Hero( tag: 'imageHero', - child: FittedBox( - fit: BoxFit.contain, - child: Image.memory(image), - ), + child: FittedBox( + fit: BoxFit.contain, + child: Image.memory(image), + ), ), ), ), floatingActionButton: FloatingActionButton( - onPressed: () async{ + onPressed: () async { final tempDir = await getTemporaryDirectory(); String fileExtension = 'jpg'; if (image[0] == 0x47) { @@ -318,4 +324,4 @@ class FullScreenImage extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/views/updateProfile.dart b/lib/views/updateProfile.dart index 547ff65..b694aca 100644 --- a/lib/views/updateProfile.dart +++ b/lib/views/updateProfile.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:forum/palette.dart'; @@ -30,7 +30,9 @@ class UpdateProfileWidgetState extends State { final imageTemp = File(image.path); setState(() => this.image = imageTemp); } on PlatformException catch (e) { - print('Failed to pick image: $e'); + if (kDebugMode) { + print('Failed to pick image: $e'); + } } } diff --git a/lib/views/user_page.dart b/lib/views/user_page.dart index 33ec686..92d2cdc 100644 --- a/lib/views/user_page.dart +++ b/lib/views/user_page.dart @@ -10,7 +10,6 @@ import 'package:forum/views/post_list_view.dart'; import 'package:forum/views/updateProfile.dart'; import 'package:go_router/go_router.dart'; -import 'login_page.dart'; class UserPage extends StatefulWidget { final String userId; From 83597d57a5e67685b975264f5c57d6c36ce827e0 Mon Sep 17 00:00:00 2001 From: finnp Date: Thu, 13 Apr 2023 21:19:40 +0200 Subject: [PATCH 3/4] fixes --- lib/views/app_bar.dart | 4 +--- lib/views/updateProfile.dart | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/views/app_bar.dart b/lib/views/app_bar.dart index 1537a91..c01ba65 100644 --- a/lib/views/app_bar.dart +++ b/lib/views/app_bar.dart @@ -3,7 +3,6 @@ import 'package:forum/models/post.dart'; import 'package:forum/palette.dart'; import 'package:forum/services/remote_services.dart'; import 'package:forum/views/editPostPage.dart'; -import 'package:forum/views/user_page.dart'; import 'package:go_router/go_router.dart'; AppBar buildAppBar(BuildContext context) { @@ -55,8 +54,7 @@ AppBar buildMainAppBar(BuildContext context) { IconButton( onPressed: () { localServices.getUserId().then((value) { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => UserPage(userId: value!))); + context.pushNamed('user', params: {'id': value.toString()}); }); }, icon: const Icon(Icons.person), diff --git a/lib/views/updateProfile.dart b/lib/views/updateProfile.dart index b694aca..bb8fef4 100644 --- a/lib/views/updateProfile.dart +++ b/lib/views/updateProfile.dart @@ -56,7 +56,7 @@ class UpdateProfileWidgetState extends State { appBar: buildAppBar(context), body: Container( padding: const EdgeInsets.all(20), - color: Palette.BlueToLight[50], + color: Palette.BlueToLight, child: Column( children: [ const SizedBox(height: 20), From 1711a8de2c5bb721d2b4c647f456afbc4a6d1cd0 Mon Sep 17 00:00:00 2001 From: finnp Date: Thu, 13 Apr 2023 21:54:08 +0200 Subject: [PATCH 4/4] added configs --- android/app/src/main/AndroidManifest.xml | 8 ++++++++ ios/Runner/Info.plist | 15 +++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 60e8014..bcee682 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -13,6 +13,14 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> + + + + + + + +