_articleList = []; 6 | bool _status = false; 7 | 8 | void setArticleList(List articleList) { 9 | _articleList = []; 10 | _articleList = articleList; 11 | notifyListeners(); 12 | } 13 | 14 | List getArticleList() { 15 | return _articleList; 16 | } 17 | 18 | void clearArticleList(){ 19 | _articleList = []; 20 | notifyListeners(); 21 | } 22 | 23 | void setloader(bool status) { 24 | _status = status; 25 | notifyListeners(); 26 | } 27 | 28 | bool getLoader() { 29 | return _status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/services/post_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class PostService { 5 | String nodeName = 'posts'; 6 | FirebaseDatabase database = FirebaseDatabase.instance; 7 | late DatabaseReference _databaseReference; 8 | 9 | void addPost(Post post) { 10 | //refrence to Posts node 11 | _databaseReference = database.reference().child(nodeName); 12 | _databaseReference.push().set(post.toMap()); 13 | } 14 | 15 | void deletePost(Post post) { 16 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 17 | _databaseReference.remove(); 18 | } 19 | 20 | void updatePost(Post post) { 21 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 22 | _databaseReference.update(post.toMap()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageViewWidget extends StatelessWidget { 4 | const PageViewWidget({ 5 | required this.image, 6 | required this.text, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final String image, text; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.all(20.0), 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Image.asset( 20 | 'assets/$image', 21 | height: 200, 22 | ), 23 | const SizedBox(height: 50), 24 | Text( 25 | text, 26 | textAlign: TextAlign.center, 27 | style: const TextStyle(fontSize: 15.0, color: Colors.black), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/helpers/ad_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AdHelper { 4 | 5 | static String get bannerAdUnitId { 6 | if (Platform.isAndroid) { 7 | return 'ca-app-pub-1408860275796619/4926645939'; 8 | } else if (Platform.isIOS) { 9 | return ''; 10 | } else { 11 | throw new UnsupportedError('Unsupported platform'); 12 | } 13 | } 14 | 15 | static String get interstitialAdUnitId { 16 | if (Platform.isAndroid) { 17 | return ''; 18 | } else if (Platform.isIOS) { 19 | return ''; 20 | } else { 21 | throw new UnsupportedError('Unsupported platform'); 22 | } 23 | } 24 | 25 | static String get rewardedAdUnitId { 26 | if (Platform.isAndroid) { 27 | return ''; 28 | } else if (Platform.isIOS) { 29 | return ''; 30 | } else { 31 | throw new UnsupportedError('Unsupported platform'); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [README](README.md) 4 | * [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) 5 | * [Contributing](CONTRIBUTING.md) 6 | * [PRIVACY\_POLICY](PRIVACY_POLICY.md) 7 | * [v1.0-copy](v1.0-copy/README.md) 8 | * [himanshu](v1.0-copy/himanshu/README.md) 9 | * [Getting Started](v1.0-copy/himanshu/getting-started.md) 10 | * [v1.0](v1.0/README.md) 11 | * [himanshu](v1.0/himanshu/README.md) 12 | * [Getting Started](v1.0/himanshu/getting-started.md) 13 | * [Interlink the page](v1.0/himanshu/interlink-the-page.md) 14 | * [Testing Nested Pages](v1.0/himanshu/testing-nested-pages.md) 15 | * [getting-started](v1.0/himanshu/getting-started-1/README.md) 16 | * [Testing Nested Pages](v1.0/himanshu/getting-started/testing-nested-pages.md) 17 | * [testing-nested-pages](v1.0/himanshu/testing-nested-pages-1/README.md) 18 | * [Testing directory](v1.0/himanshu/testing-nested-pages/testing-directory.md) 19 | * [ios](ios/README.md) 20 | * [Runner](ios/runner/README.md) 21 | * [Assets.xcassets](ios/runner/assets.xcassets/README.md) 22 | * [Launch Screen Assets](ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) 23 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles_webview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class MediumArticlesWebView extends StatefulWidget { 5 | const MediumArticlesWebView({required this.title, required this.url}); 6 | 7 | final String title; 8 | final String url; 9 | 10 | @override 11 | State createState() { 12 | return MediumArticlesWebViewState(); 13 | } 14 | } 15 | 16 | class MediumArticlesWebViewState extends State { 17 | WebViewController controller = WebViewController(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | controller = WebViewController()..loadRequest(Uri.parse(widget.url)); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: Text(widget.title), 29 | leading: IconButton( 30 | icon: const Icon(Icons.arrow_back_ios_rounded), 31 | onPressed: () => Navigator.of(context).pop(), 32 | ), 33 | ), 34 | body: WebViewWidget( 35 | controller: controller, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | When contributing to this repository, please first discuss the change you wish to make via issue, 5 | email, or any other method with the owners of this repository before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 12 | build. 13 | 2. Update the README.md with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 16 | Pull Request would represent. 17 | 18 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 19 | do not have permission to do that, you may request the second reviewer to merge it for you. 20 | 21 | ## Code of Conduct 22 | 23 | Check the code of conduct [here](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md). 24 | -------------------------------------------------------------------------------- /lib/services/shared_preference_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // this class provides basic methods to store data into 4 | // and retrieve data from shared preferences 5 | 6 | class SharedPreferencesService { 7 | static late SharedPreferences _sharedPreferences; 8 | 9 | // Create shared preferences instance here 10 | Future init() async { 11 | _sharedPreferences = await SharedPreferences.getInstance(); 12 | } 13 | 14 | static String sharedPreferenceFirstLaunchKey = 'FIRSTLAUNCH'; 15 | static String sharedPreferenceDarkThemeKey = 'DARKTHEME'; 16 | 17 | /// Set Data to Sharedpreference 18 | static Future setFirstLaunch({required bool to}) async { 19 | return _sharedPreferences.setBool(sharedPreferenceFirstLaunchKey, to); 20 | } 21 | 22 | static Future setDarkTheme({required bool to}) async { 23 | return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to); 24 | } 25 | 26 | /// Fetching Data From Sharedpreference 27 | static bool getFirstLaunch() { 28 | return _sharedPreferences.getBool(sharedPreferenceFirstLaunchKey) ?? true; 29 | } 30 | 31 | static bool getDarkTheme() { 32 | return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java~da5f7411e21bb5448b3ce32e0f034da6cf110cf8_0: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | 8 | /** 9 | * Generated file. Do not edit. 10 | * This file is generated by the Flutter tool based on the 11 | * plugins that support the Android platform. 12 | */ 13 | @Keep 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 16 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 17 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.database.FirebaseDatabasePlugin()); 18 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 20 | flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); 21 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 22 | flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest Labeler 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request: 7 | types: [opened] 8 | 9 | jobs: 10 | add-hacktoberfest-label: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add Hacktoberfest label to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | script: | 20 | await github.rest.issues.addLabels({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | issue_number: context.payload.issue.number, 24 | labels: ['hacktoberfest'] 25 | }); 26 | 27 | - name: Add Hacktoberfest label to new pull requests 28 | if: github.event_name == 'pull_request' 29 | uses: actions/github-script@v7 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | script: | 33 | await github.rest.issues.addLabels({ 34 | owner: context.repo.owner, 35 | repo: context.repo.repo, 36 | issue_number: context.payload.pull_request.number, 37 | labels: ['hacktoberfest'] 38 | }); 39 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq 9 | ANDROID_CLIENT_ID 10 | 155353198934-tn2e4dkthbeetd0h0elgh8n798pg47ng.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyDigy3xFW23DHQ8Dzdq6aj_1lBlJUi1JUI 13 | GCM_SENDER_ID 14 | 155353198934 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | blogApp 19 | PROJECT_ID 20 | blog-app-af235 21 | STORAGE_BUCKET 22 | blog-app-af235.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:155353198934:ios:43364d6a3a5afc519426f7 35 | DATABASE_URL 36 | https://blog-app-af235.firebaseio.com 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/services/fetch_medium_articles_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:blog_app/models/article.dart'; 4 | import 'package:blog_app/providers/medium_article_notifier.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class FetchMediumArticleService { 9 | static const String API_ENDPOINT = 10 | 'https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@'; 11 | 12 | static Future getPosts( 13 | MediumArticleNotifier mediumArticleNotifier, String username) async { 14 | final String url = API_ENDPOINT + username; 15 | final List articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
[]; 6 | bool _status = false; 7 | 8 | void setArticleList(List articleList) { 9 | _articleList = []; 10 | _articleList = articleList; 11 | notifyListeners(); 12 | } 13 | 14 | List getArticleList() { 15 | return _articleList; 16 | } 17 | 18 | void clearArticleList(){ 19 | _articleList = []; 20 | notifyListeners(); 21 | } 22 | 23 | void setloader(bool status) { 24 | _status = status; 25 | notifyListeners(); 26 | } 27 | 28 | bool getLoader() { 29 | return _status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/services/post_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class PostService { 5 | String nodeName = 'posts'; 6 | FirebaseDatabase database = FirebaseDatabase.instance; 7 | late DatabaseReference _databaseReference; 8 | 9 | void addPost(Post post) { 10 | //refrence to Posts node 11 | _databaseReference = database.reference().child(nodeName); 12 | _databaseReference.push().set(post.toMap()); 13 | } 14 | 15 | void deletePost(Post post) { 16 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 17 | _databaseReference.remove(); 18 | } 19 | 20 | void updatePost(Post post) { 21 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 22 | _databaseReference.update(post.toMap()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageViewWidget extends StatelessWidget { 4 | const PageViewWidget({ 5 | required this.image, 6 | required this.text, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final String image, text; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.all(20.0), 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Image.asset( 20 | 'assets/$image', 21 | height: 200, 22 | ), 23 | const SizedBox(height: 50), 24 | Text( 25 | text, 26 | textAlign: TextAlign.center, 27 | style: const TextStyle(fontSize: 15.0, color: Colors.black), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/helpers/ad_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AdHelper { 4 | 5 | static String get bannerAdUnitId { 6 | if (Platform.isAndroid) { 7 | return 'ca-app-pub-1408860275796619/4926645939'; 8 | } else if (Platform.isIOS) { 9 | return ''; 10 | } else { 11 | throw new UnsupportedError('Unsupported platform'); 12 | } 13 | } 14 | 15 | static String get interstitialAdUnitId { 16 | if (Platform.isAndroid) { 17 | return ''; 18 | } else if (Platform.isIOS) { 19 | return ''; 20 | } else { 21 | throw new UnsupportedError('Unsupported platform'); 22 | } 23 | } 24 | 25 | static String get rewardedAdUnitId { 26 | if (Platform.isAndroid) { 27 | return ''; 28 | } else if (Platform.isIOS) { 29 | return ''; 30 | } else { 31 | throw new UnsupportedError('Unsupported platform'); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [README](README.md) 4 | * [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) 5 | * [Contributing](CONTRIBUTING.md) 6 | * [PRIVACY\_POLICY](PRIVACY_POLICY.md) 7 | * [v1.0-copy](v1.0-copy/README.md) 8 | * [himanshu](v1.0-copy/himanshu/README.md) 9 | * [Getting Started](v1.0-copy/himanshu/getting-started.md) 10 | * [v1.0](v1.0/README.md) 11 | * [himanshu](v1.0/himanshu/README.md) 12 | * [Getting Started](v1.0/himanshu/getting-started.md) 13 | * [Interlink the page](v1.0/himanshu/interlink-the-page.md) 14 | * [Testing Nested Pages](v1.0/himanshu/testing-nested-pages.md) 15 | * [getting-started](v1.0/himanshu/getting-started-1/README.md) 16 | * [Testing Nested Pages](v1.0/himanshu/getting-started/testing-nested-pages.md) 17 | * [testing-nested-pages](v1.0/himanshu/testing-nested-pages-1/README.md) 18 | * [Testing directory](v1.0/himanshu/testing-nested-pages/testing-directory.md) 19 | * [ios](ios/README.md) 20 | * [Runner](ios/runner/README.md) 21 | * [Assets.xcassets](ios/runner/assets.xcassets/README.md) 22 | * [Launch Screen Assets](ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) 23 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles_webview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class MediumArticlesWebView extends StatefulWidget { 5 | const MediumArticlesWebView({required this.title, required this.url}); 6 | 7 | final String title; 8 | final String url; 9 | 10 | @override 11 | State createState() { 12 | return MediumArticlesWebViewState(); 13 | } 14 | } 15 | 16 | class MediumArticlesWebViewState extends State { 17 | WebViewController controller = WebViewController(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | controller = WebViewController()..loadRequest(Uri.parse(widget.url)); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: Text(widget.title), 29 | leading: IconButton( 30 | icon: const Icon(Icons.arrow_back_ios_rounded), 31 | onPressed: () => Navigator.of(context).pop(), 32 | ), 33 | ), 34 | body: WebViewWidget( 35 | controller: controller, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | When contributing to this repository, please first discuss the change you wish to make via issue, 5 | email, or any other method with the owners of this repository before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 12 | build. 13 | 2. Update the README.md with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 16 | Pull Request would represent. 17 | 18 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 19 | do not have permission to do that, you may request the second reviewer to merge it for you. 20 | 21 | ## Code of Conduct 22 | 23 | Check the code of conduct [here](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md). 24 | -------------------------------------------------------------------------------- /lib/services/shared_preference_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // this class provides basic methods to store data into 4 | // and retrieve data from shared preferences 5 | 6 | class SharedPreferencesService { 7 | static late SharedPreferences _sharedPreferences; 8 | 9 | // Create shared preferences instance here 10 | Future init() async { 11 | _sharedPreferences = await SharedPreferences.getInstance(); 12 | } 13 | 14 | static String sharedPreferenceFirstLaunchKey = 'FIRSTLAUNCH'; 15 | static String sharedPreferenceDarkThemeKey = 'DARKTHEME'; 16 | 17 | /// Set Data to Sharedpreference 18 | static Future setFirstLaunch({required bool to}) async { 19 | return _sharedPreferences.setBool(sharedPreferenceFirstLaunchKey, to); 20 | } 21 | 22 | static Future setDarkTheme({required bool to}) async { 23 | return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to); 24 | } 25 | 26 | /// Fetching Data From Sharedpreference 27 | static bool getFirstLaunch() { 28 | return _sharedPreferences.getBool(sharedPreferenceFirstLaunchKey) ?? true; 29 | } 30 | 31 | static bool getDarkTheme() { 32 | return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java~da5f7411e21bb5448b3ce32e0f034da6cf110cf8_0: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | 8 | /** 9 | * Generated file. Do not edit. 10 | * This file is generated by the Flutter tool based on the 11 | * plugins that support the Android platform. 12 | */ 13 | @Keep 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 16 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 17 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.database.FirebaseDatabasePlugin()); 18 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 20 | flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); 21 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 22 | flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest Labeler 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request: 7 | types: [opened] 8 | 9 | jobs: 10 | add-hacktoberfest-label: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add Hacktoberfest label to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | script: | 20 | await github.rest.issues.addLabels({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | issue_number: context.payload.issue.number, 24 | labels: ['hacktoberfest'] 25 | }); 26 | 27 | - name: Add Hacktoberfest label to new pull requests 28 | if: github.event_name == 'pull_request' 29 | uses: actions/github-script@v7 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | script: | 33 | await github.rest.issues.addLabels({ 34 | owner: context.repo.owner, 35 | repo: context.repo.repo, 36 | issue_number: context.payload.pull_request.number, 37 | labels: ['hacktoberfest'] 38 | }); 39 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq 9 | ANDROID_CLIENT_ID 10 | 155353198934-tn2e4dkthbeetd0h0elgh8n798pg47ng.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyDigy3xFW23DHQ8Dzdq6aj_1lBlJUi1JUI 13 | GCM_SENDER_ID 14 | 155353198934 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | blogApp 19 | PROJECT_ID 20 | blog-app-af235 21 | STORAGE_BUCKET 22 | blog-app-af235.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:155353198934:ios:43364d6a3a5afc519426f7 35 | DATABASE_URL 36 | https://blog-app-af235.firebaseio.com 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/services/fetch_medium_articles_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:blog_app/models/article.dart'; 4 | import 'package:blog_app/providers/medium_article_notifier.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class FetchMediumArticleService { 9 | static const String API_ENDPOINT = 10 | 'https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@'; 11 | 12 | static Future getPosts( 13 | MediumArticleNotifier mediumArticleNotifier, String username) async { 14 | final String url = API_ENDPOINT + username; 15 | final List articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
articleList) { 9 | _articleList = []; 10 | _articleList = articleList; 11 | notifyListeners(); 12 | } 13 | 14 | List getArticleList() { 15 | return _articleList; 16 | } 17 | 18 | void clearArticleList(){ 19 | _articleList = []; 20 | notifyListeners(); 21 | } 22 | 23 | void setloader(bool status) { 24 | _status = status; 25 | notifyListeners(); 26 | } 27 | 28 | bool getLoader() { 29 | return _status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/services/post_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class PostService { 5 | String nodeName = 'posts'; 6 | FirebaseDatabase database = FirebaseDatabase.instance; 7 | late DatabaseReference _databaseReference; 8 | 9 | void addPost(Post post) { 10 | //refrence to Posts node 11 | _databaseReference = database.reference().child(nodeName); 12 | _databaseReference.push().set(post.toMap()); 13 | } 14 | 15 | void deletePost(Post post) { 16 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 17 | _databaseReference.remove(); 18 | } 19 | 20 | void updatePost(Post post) { 21 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 22 | _databaseReference.update(post.toMap()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageViewWidget extends StatelessWidget { 4 | const PageViewWidget({ 5 | required this.image, 6 | required this.text, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final String image, text; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.all(20.0), 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Image.asset( 20 | 'assets/$image', 21 | height: 200, 22 | ), 23 | const SizedBox(height: 50), 24 | Text( 25 | text, 26 | textAlign: TextAlign.center, 27 | style: const TextStyle(fontSize: 15.0, color: Colors.black), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/helpers/ad_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AdHelper { 4 | 5 | static String get bannerAdUnitId { 6 | if (Platform.isAndroid) { 7 | return 'ca-app-pub-1408860275796619/4926645939'; 8 | } else if (Platform.isIOS) { 9 | return ''; 10 | } else { 11 | throw new UnsupportedError('Unsupported platform'); 12 | } 13 | } 14 | 15 | static String get interstitialAdUnitId { 16 | if (Platform.isAndroid) { 17 | return ''; 18 | } else if (Platform.isIOS) { 19 | return ''; 20 | } else { 21 | throw new UnsupportedError('Unsupported platform'); 22 | } 23 | } 24 | 25 | static String get rewardedAdUnitId { 26 | if (Platform.isAndroid) { 27 | return ''; 28 | } else if (Platform.isIOS) { 29 | return ''; 30 | } else { 31 | throw new UnsupportedError('Unsupported platform'); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [README](README.md) 4 | * [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) 5 | * [Contributing](CONTRIBUTING.md) 6 | * [PRIVACY\_POLICY](PRIVACY_POLICY.md) 7 | * [v1.0-copy](v1.0-copy/README.md) 8 | * [himanshu](v1.0-copy/himanshu/README.md) 9 | * [Getting Started](v1.0-copy/himanshu/getting-started.md) 10 | * [v1.0](v1.0/README.md) 11 | * [himanshu](v1.0/himanshu/README.md) 12 | * [Getting Started](v1.0/himanshu/getting-started.md) 13 | * [Interlink the page](v1.0/himanshu/interlink-the-page.md) 14 | * [Testing Nested Pages](v1.0/himanshu/testing-nested-pages.md) 15 | * [getting-started](v1.0/himanshu/getting-started-1/README.md) 16 | * [Testing Nested Pages](v1.0/himanshu/getting-started/testing-nested-pages.md) 17 | * [testing-nested-pages](v1.0/himanshu/testing-nested-pages-1/README.md) 18 | * [Testing directory](v1.0/himanshu/testing-nested-pages/testing-directory.md) 19 | * [ios](ios/README.md) 20 | * [Runner](ios/runner/README.md) 21 | * [Assets.xcassets](ios/runner/assets.xcassets/README.md) 22 | * [Launch Screen Assets](ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) 23 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles_webview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class MediumArticlesWebView extends StatefulWidget { 5 | const MediumArticlesWebView({required this.title, required this.url}); 6 | 7 | final String title; 8 | final String url; 9 | 10 | @override 11 | State createState() { 12 | return MediumArticlesWebViewState(); 13 | } 14 | } 15 | 16 | class MediumArticlesWebViewState extends State { 17 | WebViewController controller = WebViewController(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | controller = WebViewController()..loadRequest(Uri.parse(widget.url)); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: Text(widget.title), 29 | leading: IconButton( 30 | icon: const Icon(Icons.arrow_back_ios_rounded), 31 | onPressed: () => Navigator.of(context).pop(), 32 | ), 33 | ), 34 | body: WebViewWidget( 35 | controller: controller, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | When contributing to this repository, please first discuss the change you wish to make via issue, 5 | email, or any other method with the owners of this repository before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 12 | build. 13 | 2. Update the README.md with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 16 | Pull Request would represent. 17 | 18 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 19 | do not have permission to do that, you may request the second reviewer to merge it for you. 20 | 21 | ## Code of Conduct 22 | 23 | Check the code of conduct [here](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md). 24 | -------------------------------------------------------------------------------- /lib/services/shared_preference_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // this class provides basic methods to store data into 4 | // and retrieve data from shared preferences 5 | 6 | class SharedPreferencesService { 7 | static late SharedPreferences _sharedPreferences; 8 | 9 | // Create shared preferences instance here 10 | Future init() async { 11 | _sharedPreferences = await SharedPreferences.getInstance(); 12 | } 13 | 14 | static String sharedPreferenceFirstLaunchKey = 'FIRSTLAUNCH'; 15 | static String sharedPreferenceDarkThemeKey = 'DARKTHEME'; 16 | 17 | /// Set Data to Sharedpreference 18 | static Future setFirstLaunch({required bool to}) async { 19 | return _sharedPreferences.setBool(sharedPreferenceFirstLaunchKey, to); 20 | } 21 | 22 | static Future setDarkTheme({required bool to}) async { 23 | return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to); 24 | } 25 | 26 | /// Fetching Data From Sharedpreference 27 | static bool getFirstLaunch() { 28 | return _sharedPreferences.getBool(sharedPreferenceFirstLaunchKey) ?? true; 29 | } 30 | 31 | static bool getDarkTheme() { 32 | return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java~da5f7411e21bb5448b3ce32e0f034da6cf110cf8_0: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | 8 | /** 9 | * Generated file. Do not edit. 10 | * This file is generated by the Flutter tool based on the 11 | * plugins that support the Android platform. 12 | */ 13 | @Keep 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 16 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 17 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.database.FirebaseDatabasePlugin()); 18 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 20 | flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); 21 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 22 | flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest Labeler 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request: 7 | types: [opened] 8 | 9 | jobs: 10 | add-hacktoberfest-label: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add Hacktoberfest label to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | script: | 20 | await github.rest.issues.addLabels({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | issue_number: context.payload.issue.number, 24 | labels: ['hacktoberfest'] 25 | }); 26 | 27 | - name: Add Hacktoberfest label to new pull requests 28 | if: github.event_name == 'pull_request' 29 | uses: actions/github-script@v7 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | script: | 33 | await github.rest.issues.addLabels({ 34 | owner: context.repo.owner, 35 | repo: context.repo.repo, 36 | issue_number: context.payload.pull_request.number, 37 | labels: ['hacktoberfest'] 38 | }); 39 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq 9 | ANDROID_CLIENT_ID 10 | 155353198934-tn2e4dkthbeetd0h0elgh8n798pg47ng.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyDigy3xFW23DHQ8Dzdq6aj_1lBlJUi1JUI 13 | GCM_SENDER_ID 14 | 155353198934 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | blogApp 19 | PROJECT_ID 20 | blog-app-af235 21 | STORAGE_BUCKET 22 | blog-app-af235.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:155353198934:ios:43364d6a3a5afc519426f7 35 | DATABASE_URL 36 | https://blog-app-af235.firebaseio.com 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/services/fetch_medium_articles_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:blog_app/models/article.dart'; 4 | import 'package:blog_app/providers/medium_article_notifier.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class FetchMediumArticleService { 9 | static const String API_ENDPOINT = 10 | 'https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@'; 11 | 12 | static Future getPosts( 13 | MediumArticleNotifier mediumArticleNotifier, String username) async { 14 | final String url = API_ENDPOINT + username; 15 | final List articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
[]; 10 | _articleList = articleList; 11 | notifyListeners(); 12 | } 13 | 14 | List getArticleList() { 15 | return _articleList; 16 | } 17 | 18 | void clearArticleList(){ 19 | _articleList = []; 20 | notifyListeners(); 21 | } 22 | 23 | void setloader(bool status) { 24 | _status = status; 25 | notifyListeners(); 26 | } 27 | 28 | bool getLoader() { 29 | return _status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/services/post_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class PostService { 5 | String nodeName = 'posts'; 6 | FirebaseDatabase database = FirebaseDatabase.instance; 7 | late DatabaseReference _databaseReference; 8 | 9 | void addPost(Post post) { 10 | //refrence to Posts node 11 | _databaseReference = database.reference().child(nodeName); 12 | _databaseReference.push().set(post.toMap()); 13 | } 14 | 15 | void deletePost(Post post) { 16 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 17 | _databaseReference.remove(); 18 | } 19 | 20 | void updatePost(Post post) { 21 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 22 | _databaseReference.update(post.toMap()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageViewWidget extends StatelessWidget { 4 | const PageViewWidget({ 5 | required this.image, 6 | required this.text, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final String image, text; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.all(20.0), 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Image.asset( 20 | 'assets/$image', 21 | height: 200, 22 | ), 23 | const SizedBox(height: 50), 24 | Text( 25 | text, 26 | textAlign: TextAlign.center, 27 | style: const TextStyle(fontSize: 15.0, color: Colors.black), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/helpers/ad_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AdHelper { 4 | 5 | static String get bannerAdUnitId { 6 | if (Platform.isAndroid) { 7 | return 'ca-app-pub-1408860275796619/4926645939'; 8 | } else if (Platform.isIOS) { 9 | return ''; 10 | } else { 11 | throw new UnsupportedError('Unsupported platform'); 12 | } 13 | } 14 | 15 | static String get interstitialAdUnitId { 16 | if (Platform.isAndroid) { 17 | return ''; 18 | } else if (Platform.isIOS) { 19 | return ''; 20 | } else { 21 | throw new UnsupportedError('Unsupported platform'); 22 | } 23 | } 24 | 25 | static String get rewardedAdUnitId { 26 | if (Platform.isAndroid) { 27 | return ''; 28 | } else if (Platform.isIOS) { 29 | return ''; 30 | } else { 31 | throw new UnsupportedError('Unsupported platform'); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [README](README.md) 4 | * [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) 5 | * [Contributing](CONTRIBUTING.md) 6 | * [PRIVACY\_POLICY](PRIVACY_POLICY.md) 7 | * [v1.0-copy](v1.0-copy/README.md) 8 | * [himanshu](v1.0-copy/himanshu/README.md) 9 | * [Getting Started](v1.0-copy/himanshu/getting-started.md) 10 | * [v1.0](v1.0/README.md) 11 | * [himanshu](v1.0/himanshu/README.md) 12 | * [Getting Started](v1.0/himanshu/getting-started.md) 13 | * [Interlink the page](v1.0/himanshu/interlink-the-page.md) 14 | * [Testing Nested Pages](v1.0/himanshu/testing-nested-pages.md) 15 | * [getting-started](v1.0/himanshu/getting-started-1/README.md) 16 | * [Testing Nested Pages](v1.0/himanshu/getting-started/testing-nested-pages.md) 17 | * [testing-nested-pages](v1.0/himanshu/testing-nested-pages-1/README.md) 18 | * [Testing directory](v1.0/himanshu/testing-nested-pages/testing-directory.md) 19 | * [ios](ios/README.md) 20 | * [Runner](ios/runner/README.md) 21 | * [Assets.xcassets](ios/runner/assets.xcassets/README.md) 22 | * [Launch Screen Assets](ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) 23 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles_webview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class MediumArticlesWebView extends StatefulWidget { 5 | const MediumArticlesWebView({required this.title, required this.url}); 6 | 7 | final String title; 8 | final String url; 9 | 10 | @override 11 | State createState() { 12 | return MediumArticlesWebViewState(); 13 | } 14 | } 15 | 16 | class MediumArticlesWebViewState extends State { 17 | WebViewController controller = WebViewController(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | controller = WebViewController()..loadRequest(Uri.parse(widget.url)); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: Text(widget.title), 29 | leading: IconButton( 30 | icon: const Icon(Icons.arrow_back_ios_rounded), 31 | onPressed: () => Navigator.of(context).pop(), 32 | ), 33 | ), 34 | body: WebViewWidget( 35 | controller: controller, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | When contributing to this repository, please first discuss the change you wish to make via issue, 5 | email, or any other method with the owners of this repository before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 12 | build. 13 | 2. Update the README.md with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 16 | Pull Request would represent. 17 | 18 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 19 | do not have permission to do that, you may request the second reviewer to merge it for you. 20 | 21 | ## Code of Conduct 22 | 23 | Check the code of conduct [here](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md). 24 | -------------------------------------------------------------------------------- /lib/services/shared_preference_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // this class provides basic methods to store data into 4 | // and retrieve data from shared preferences 5 | 6 | class SharedPreferencesService { 7 | static late SharedPreferences _sharedPreferences; 8 | 9 | // Create shared preferences instance here 10 | Future init() async { 11 | _sharedPreferences = await SharedPreferences.getInstance(); 12 | } 13 | 14 | static String sharedPreferenceFirstLaunchKey = 'FIRSTLAUNCH'; 15 | static String sharedPreferenceDarkThemeKey = 'DARKTHEME'; 16 | 17 | /// Set Data to Sharedpreference 18 | static Future setFirstLaunch({required bool to}) async { 19 | return _sharedPreferences.setBool(sharedPreferenceFirstLaunchKey, to); 20 | } 21 | 22 | static Future setDarkTheme({required bool to}) async { 23 | return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to); 24 | } 25 | 26 | /// Fetching Data From Sharedpreference 27 | static bool getFirstLaunch() { 28 | return _sharedPreferences.getBool(sharedPreferenceFirstLaunchKey) ?? true; 29 | } 30 | 31 | static bool getDarkTheme() { 32 | return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java~da5f7411e21bb5448b3ce32e0f034da6cf110cf8_0: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | 8 | /** 9 | * Generated file. Do not edit. 10 | * This file is generated by the Flutter tool based on the 11 | * plugins that support the Android platform. 12 | */ 13 | @Keep 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 16 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 17 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.database.FirebaseDatabasePlugin()); 18 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 20 | flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); 21 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 22 | flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest Labeler 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request: 7 | types: [opened] 8 | 9 | jobs: 10 | add-hacktoberfest-label: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add Hacktoberfest label to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | script: | 20 | await github.rest.issues.addLabels({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | issue_number: context.payload.issue.number, 24 | labels: ['hacktoberfest'] 25 | }); 26 | 27 | - name: Add Hacktoberfest label to new pull requests 28 | if: github.event_name == 'pull_request' 29 | uses: actions/github-script@v7 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | script: | 33 | await github.rest.issues.addLabels({ 34 | owner: context.repo.owner, 35 | repo: context.repo.repo, 36 | issue_number: context.payload.pull_request.number, 37 | labels: ['hacktoberfest'] 38 | }); 39 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq 9 | ANDROID_CLIENT_ID 10 | 155353198934-tn2e4dkthbeetd0h0elgh8n798pg47ng.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyDigy3xFW23DHQ8Dzdq6aj_1lBlJUi1JUI 13 | GCM_SENDER_ID 14 | 155353198934 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | blogApp 19 | PROJECT_ID 20 | blog-app-af235 21 | STORAGE_BUCKET 22 | blog-app-af235.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:155353198934:ios:43364d6a3a5afc519426f7 35 | DATABASE_URL 36 | https://blog-app-af235.firebaseio.com 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/services/fetch_medium_articles_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:blog_app/models/article.dart'; 4 | import 'package:blog_app/providers/medium_article_notifier.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class FetchMediumArticleService { 9 | static const String API_ENDPOINT = 10 | 'https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@'; 11 | 12 | static Future getPosts( 13 | MediumArticleNotifier mediumArticleNotifier, String username) async { 14 | final String url = API_ENDPOINT + username; 15 | final List articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
getArticleList() { 15 | return _articleList; 16 | } 17 | 18 | void clearArticleList(){ 19 | _articleList = []; 20 | notifyListeners(); 21 | } 22 | 23 | void setloader(bool status) { 24 | _status = status; 25 | notifyListeners(); 26 | } 27 | 28 | bool getLoader() { 29 | return _status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/services/post_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class PostService { 5 | String nodeName = 'posts'; 6 | FirebaseDatabase database = FirebaseDatabase.instance; 7 | late DatabaseReference _databaseReference; 8 | 9 | void addPost(Post post) { 10 | //refrence to Posts node 11 | _databaseReference = database.reference().child(nodeName); 12 | _databaseReference.push().set(post.toMap()); 13 | } 14 | 15 | void deletePost(Post post) { 16 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 17 | _databaseReference.remove(); 18 | } 19 | 20 | void updatePost(Post post) { 21 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 22 | _databaseReference.update(post.toMap()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageViewWidget extends StatelessWidget { 4 | const PageViewWidget({ 5 | required this.image, 6 | required this.text, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final String image, text; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.all(20.0), 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Image.asset( 20 | 'assets/$image', 21 | height: 200, 22 | ), 23 | const SizedBox(height: 50), 24 | Text( 25 | text, 26 | textAlign: TextAlign.center, 27 | style: const TextStyle(fontSize: 15.0, color: Colors.black), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/helpers/ad_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AdHelper { 4 | 5 | static String get bannerAdUnitId { 6 | if (Platform.isAndroid) { 7 | return 'ca-app-pub-1408860275796619/4926645939'; 8 | } else if (Platform.isIOS) { 9 | return ''; 10 | } else { 11 | throw new UnsupportedError('Unsupported platform'); 12 | } 13 | } 14 | 15 | static String get interstitialAdUnitId { 16 | if (Platform.isAndroid) { 17 | return ''; 18 | } else if (Platform.isIOS) { 19 | return ''; 20 | } else { 21 | throw new UnsupportedError('Unsupported platform'); 22 | } 23 | } 24 | 25 | static String get rewardedAdUnitId { 26 | if (Platform.isAndroid) { 27 | return ''; 28 | } else if (Platform.isIOS) { 29 | return ''; 30 | } else { 31 | throw new UnsupportedError('Unsupported platform'); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [README](README.md) 4 | * [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) 5 | * [Contributing](CONTRIBUTING.md) 6 | * [PRIVACY\_POLICY](PRIVACY_POLICY.md) 7 | * [v1.0-copy](v1.0-copy/README.md) 8 | * [himanshu](v1.0-copy/himanshu/README.md) 9 | * [Getting Started](v1.0-copy/himanshu/getting-started.md) 10 | * [v1.0](v1.0/README.md) 11 | * [himanshu](v1.0/himanshu/README.md) 12 | * [Getting Started](v1.0/himanshu/getting-started.md) 13 | * [Interlink the page](v1.0/himanshu/interlink-the-page.md) 14 | * [Testing Nested Pages](v1.0/himanshu/testing-nested-pages.md) 15 | * [getting-started](v1.0/himanshu/getting-started-1/README.md) 16 | * [Testing Nested Pages](v1.0/himanshu/getting-started/testing-nested-pages.md) 17 | * [testing-nested-pages](v1.0/himanshu/testing-nested-pages-1/README.md) 18 | * [Testing directory](v1.0/himanshu/testing-nested-pages/testing-directory.md) 19 | * [ios](ios/README.md) 20 | * [Runner](ios/runner/README.md) 21 | * [Assets.xcassets](ios/runner/assets.xcassets/README.md) 22 | * [Launch Screen Assets](ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) 23 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles_webview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class MediumArticlesWebView extends StatefulWidget { 5 | const MediumArticlesWebView({required this.title, required this.url}); 6 | 7 | final String title; 8 | final String url; 9 | 10 | @override 11 | State createState() { 12 | return MediumArticlesWebViewState(); 13 | } 14 | } 15 | 16 | class MediumArticlesWebViewState extends State { 17 | WebViewController controller = WebViewController(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | controller = WebViewController()..loadRequest(Uri.parse(widget.url)); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: Text(widget.title), 29 | leading: IconButton( 30 | icon: const Icon(Icons.arrow_back_ios_rounded), 31 | onPressed: () => Navigator.of(context).pop(), 32 | ), 33 | ), 34 | body: WebViewWidget( 35 | controller: controller, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | When contributing to this repository, please first discuss the change you wish to make via issue, 5 | email, or any other method with the owners of this repository before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 12 | build. 13 | 2. Update the README.md with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 16 | Pull Request would represent. 17 | 18 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 19 | do not have permission to do that, you may request the second reviewer to merge it for you. 20 | 21 | ## Code of Conduct 22 | 23 | Check the code of conduct [here](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md). 24 | -------------------------------------------------------------------------------- /lib/services/shared_preference_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // this class provides basic methods to store data into 4 | // and retrieve data from shared preferences 5 | 6 | class SharedPreferencesService { 7 | static late SharedPreferences _sharedPreferences; 8 | 9 | // Create shared preferences instance here 10 | Future init() async { 11 | _sharedPreferences = await SharedPreferences.getInstance(); 12 | } 13 | 14 | static String sharedPreferenceFirstLaunchKey = 'FIRSTLAUNCH'; 15 | static String sharedPreferenceDarkThemeKey = 'DARKTHEME'; 16 | 17 | /// Set Data to Sharedpreference 18 | static Future setFirstLaunch({required bool to}) async { 19 | return _sharedPreferences.setBool(sharedPreferenceFirstLaunchKey, to); 20 | } 21 | 22 | static Future setDarkTheme({required bool to}) async { 23 | return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to); 24 | } 25 | 26 | /// Fetching Data From Sharedpreference 27 | static bool getFirstLaunch() { 28 | return _sharedPreferences.getBool(sharedPreferenceFirstLaunchKey) ?? true; 29 | } 30 | 31 | static bool getDarkTheme() { 32 | return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java~da5f7411e21bb5448b3ce32e0f034da6cf110cf8_0: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | 8 | /** 9 | * Generated file. Do not edit. 10 | * This file is generated by the Flutter tool based on the 11 | * plugins that support the Android platform. 12 | */ 13 | @Keep 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 16 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 17 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.database.FirebaseDatabasePlugin()); 18 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 20 | flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); 21 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 22 | flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest Labeler 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request: 7 | types: [opened] 8 | 9 | jobs: 10 | add-hacktoberfest-label: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add Hacktoberfest label to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | script: | 20 | await github.rest.issues.addLabels({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | issue_number: context.payload.issue.number, 24 | labels: ['hacktoberfest'] 25 | }); 26 | 27 | - name: Add Hacktoberfest label to new pull requests 28 | if: github.event_name == 'pull_request' 29 | uses: actions/github-script@v7 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | script: | 33 | await github.rest.issues.addLabels({ 34 | owner: context.repo.owner, 35 | repo: context.repo.repo, 36 | issue_number: context.payload.pull_request.number, 37 | labels: ['hacktoberfest'] 38 | }); 39 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq 9 | ANDROID_CLIENT_ID 10 | 155353198934-tn2e4dkthbeetd0h0elgh8n798pg47ng.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyDigy3xFW23DHQ8Dzdq6aj_1lBlJUi1JUI 13 | GCM_SENDER_ID 14 | 155353198934 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | blogApp 19 | PROJECT_ID 20 | blog-app-af235 21 | STORAGE_BUCKET 22 | blog-app-af235.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:155353198934:ios:43364d6a3a5afc519426f7 35 | DATABASE_URL 36 | https://blog-app-af235.firebaseio.com 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/services/fetch_medium_articles_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:blog_app/models/article.dart'; 4 | import 'package:blog_app/providers/medium_article_notifier.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class FetchMediumArticleService { 9 | static const String API_ENDPOINT = 10 | 'https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@'; 11 | 12 | static Future getPosts( 13 | MediumArticleNotifier mediumArticleNotifier, String username) async { 14 | final String url = API_ENDPOINT + username; 15 | final List articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
[]; 20 | notifyListeners(); 21 | } 22 | 23 | void setloader(bool status) { 24 | _status = status; 25 | notifyListeners(); 26 | } 27 | 28 | bool getLoader() { 29 | return _status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/services/post_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:firebase_database/firebase_database.dart'; 3 | 4 | class PostService { 5 | String nodeName = 'posts'; 6 | FirebaseDatabase database = FirebaseDatabase.instance; 7 | late DatabaseReference _databaseReference; 8 | 9 | void addPost(Post post) { 10 | //refrence to Posts node 11 | _databaseReference = database.reference().child(nodeName); 12 | _databaseReference.push().set(post.toMap()); 13 | } 14 | 15 | void deletePost(Post post) { 16 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 17 | _databaseReference.remove(); 18 | } 19 | 20 | void updatePost(Post post) { 21 | _databaseReference = database.reference().child('$nodeName/${post.key}'); 22 | _databaseReference.update(post.toMap()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/widgets/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageViewWidget extends StatelessWidget { 4 | const PageViewWidget({ 5 | required this.image, 6 | required this.text, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final String image, text; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.all(20.0), 16 | child: Column( 17 | mainAxisAlignment: MainAxisAlignment.center, 18 | children: [ 19 | Image.asset( 20 | 'assets/$image', 21 | height: 200, 22 | ), 23 | const SizedBox(height: 50), 24 | Text( 25 | text, 26 | textAlign: TextAlign.center, 27 | style: const TextStyle(fontSize: 15.0, color: Colors.black), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/helpers/ad_helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class AdHelper { 4 | 5 | static String get bannerAdUnitId { 6 | if (Platform.isAndroid) { 7 | return 'ca-app-pub-1408860275796619/4926645939'; 8 | } else if (Platform.isIOS) { 9 | return ''; 10 | } else { 11 | throw new UnsupportedError('Unsupported platform'); 12 | } 13 | } 14 | 15 | static String get interstitialAdUnitId { 16 | if (Platform.isAndroid) { 17 | return ''; 18 | } else if (Platform.isIOS) { 19 | return ''; 20 | } else { 21 | throw new UnsupportedError('Unsupported platform'); 22 | } 23 | } 24 | 25 | static String get rewardedAdUnitId { 26 | if (Platform.isAndroid) { 27 | return ''; 28 | } else if (Platform.isIOS) { 29 | return ''; 30 | } else { 31 | throw new UnsupportedError('Unsupported platform'); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [README](README.md) 4 | * [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md) 5 | * [Contributing](CONTRIBUTING.md) 6 | * [PRIVACY\_POLICY](PRIVACY_POLICY.md) 7 | * [v1.0-copy](v1.0-copy/README.md) 8 | * [himanshu](v1.0-copy/himanshu/README.md) 9 | * [Getting Started](v1.0-copy/himanshu/getting-started.md) 10 | * [v1.0](v1.0/README.md) 11 | * [himanshu](v1.0/himanshu/README.md) 12 | * [Getting Started](v1.0/himanshu/getting-started.md) 13 | * [Interlink the page](v1.0/himanshu/interlink-the-page.md) 14 | * [Testing Nested Pages](v1.0/himanshu/testing-nested-pages.md) 15 | * [getting-started](v1.0/himanshu/getting-started-1/README.md) 16 | * [Testing Nested Pages](v1.0/himanshu/getting-started/testing-nested-pages.md) 17 | * [testing-nested-pages](v1.0/himanshu/testing-nested-pages-1/README.md) 18 | * [Testing directory](v1.0/himanshu/testing-nested-pages/testing-directory.md) 19 | * [ios](ios/README.md) 20 | * [Runner](ios/runner/README.md) 21 | * [Assets.xcassets](ios/runner/assets.xcassets/README.md) 22 | * [Launch Screen Assets](ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md) 23 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles_webview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class MediumArticlesWebView extends StatefulWidget { 5 | const MediumArticlesWebView({required this.title, required this.url}); 6 | 7 | final String title; 8 | final String url; 9 | 10 | @override 11 | State createState() { 12 | return MediumArticlesWebViewState(); 13 | } 14 | } 15 | 16 | class MediumArticlesWebViewState extends State { 17 | WebViewController controller = WebViewController(); 18 | @override 19 | void initState() { 20 | super.initState(); 21 | controller = WebViewController()..loadRequest(Uri.parse(widget.url)); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: Text(widget.title), 29 | leading: IconButton( 30 | icon: const Icon(Icons.arrow_back_ios_rounded), 31 | onPressed: () => Navigator.of(context).pop(), 32 | ), 33 | ), 34 | body: WebViewWidget( 35 | controller: controller, 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | When contributing to this repository, please first discuss the change you wish to make via issue, 5 | email, or any other method with the owners of this repository before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions with the project. 8 | 9 | ## Pull Request Process 10 | 11 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 12 | build. 13 | 2. Update the README.md with details of changes to the interface, this includes new environment 14 | variables, exposed ports, useful file locations and container parameters. 15 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 16 | Pull Request would represent. 17 | 18 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 19 | do not have permission to do that, you may request the second reviewer to merge it for you. 20 | 21 | ## Code of Conduct 22 | 23 | Check the code of conduct [here](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md). 24 | -------------------------------------------------------------------------------- /lib/services/shared_preference_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | // this class provides basic methods to store data into 4 | // and retrieve data from shared preferences 5 | 6 | class SharedPreferencesService { 7 | static late SharedPreferences _sharedPreferences; 8 | 9 | // Create shared preferences instance here 10 | Future init() async { 11 | _sharedPreferences = await SharedPreferences.getInstance(); 12 | } 13 | 14 | static String sharedPreferenceFirstLaunchKey = 'FIRSTLAUNCH'; 15 | static String sharedPreferenceDarkThemeKey = 'DARKTHEME'; 16 | 17 | /// Set Data to Sharedpreference 18 | static Future setFirstLaunch({required bool to}) async { 19 | return _sharedPreferences.setBool(sharedPreferenceFirstLaunchKey, to); 20 | } 21 | 22 | static Future setDarkTheme({required bool to}) async { 23 | return _sharedPreferences.setBool(sharedPreferenceDarkThemeKey, to); 24 | } 25 | 26 | /// Fetching Data From Sharedpreference 27 | static bool getFirstLaunch() { 28 | return _sharedPreferences.getBool(sharedPreferenceFirstLaunchKey) ?? true; 29 | } 30 | 31 | static bool getDarkTheme() { 32 | return _sharedPreferences.getBool(sharedPreferenceDarkThemeKey) ?? false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java~da5f7411e21bb5448b3ce32e0f034da6cf110cf8_0: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import androidx.annotation.Keep; 4 | import androidx.annotation.NonNull; 5 | 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | 8 | /** 9 | * Generated file. Do not edit. 10 | * This file is generated by the Flutter tool based on the 11 | * plugins that support the Android platform. 12 | */ 13 | @Keep 14 | public final class GeneratedPluginRegistrant { 15 | public static void registerWith(@NonNull FlutterEngine flutterEngine) { 16 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.core.FlutterFirebaseCorePlugin()); 17 | flutterEngine.getPlugins().add(new io.flutter.plugins.firebase.database.FirebaseDatabasePlugin()); 18 | flutterEngine.getPlugins().add(new io.flutter.plugins.pathprovider.PathProviderPlugin()); 19 | flutterEngine.getPlugins().add(new io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin()); 20 | flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin()); 21 | flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin()); 22 | flutterEngine.getPlugins().add(new io.flutter.plugins.webviewflutter.WebViewFlutterPlugin()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/hacktoberfest-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Hacktoberfest Labeler 2 | 3 | on: 4 | issues: 5 | types: [opened] 6 | pull_request: 7 | types: [opened] 8 | 9 | jobs: 10 | add-hacktoberfest-label: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Add Hacktoberfest label to new issues 15 | if: github.event_name == 'issues' 16 | uses: actions/github-script@v7 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | script: | 20 | await github.rest.issues.addLabels({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | issue_number: context.payload.issue.number, 24 | labels: ['hacktoberfest'] 25 | }); 26 | 27 | - name: Add Hacktoberfest label to new pull requests 28 | if: github.event_name == 'pull_request' 29 | uses: actions/github-script@v7 30 | with: 31 | github-token: ${{ secrets.GITHUB_TOKEN }} 32 | script: | 33 | await github.rest.issues.addLabels({ 34 | owner: context.repo.owner, 35 | repo: context.repo.repo, 36 | issue_number: context.payload.pull_request.number, 37 | labels: ['hacktoberfest'] 38 | }); 39 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq 9 | ANDROID_CLIENT_ID 10 | 155353198934-tn2e4dkthbeetd0h0elgh8n798pg47ng.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyDigy3xFW23DHQ8Dzdq6aj_1lBlJUi1JUI 13 | GCM_SENDER_ID 14 | 155353198934 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | blogApp 19 | PROJECT_ID 20 | blog-app-af235 21 | STORAGE_BUCKET 22 | blog-app-af235.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:155353198934:ios:43364d6a3a5afc519426f7 35 | DATABASE_URL 36 | https://blog-app-af235.firebaseio.com 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/services/fetch_medium_articles_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:blog_app/models/article.dart'; 4 | import 'package:blog_app/providers/medium_article_notifier.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class FetchMediumArticleService { 9 | static const String API_ENDPOINT = 10 | 'https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/@'; 11 | 12 | static Future getPosts( 13 | MediumArticleNotifier mediumArticleNotifier, String username) async { 14 | final String url = API_ENDPOINT + username; 15 | final List articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
articleList = []; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
[]; 16 | http.get(Uri.parse(url)).then( 17 | (http.Response response) { 18 | debugPrint('Response status: ${response.statusCode}'); 19 | if (response.statusCode == 200) { 20 | final List> posts = 21 | new List>.from( 22 | jsonDecode(response.body)["items"]); 23 | posts.forEach( 24 | (Map element) { 25 | articleList.add( 26 | Article.fromMap(element), 27 | ); 28 | }, 29 | ); 30 | mediumArticleNotifier.setloader(true); 31 | mediumArticleNotifier.setArticleList(articleList); 32 | } else { 33 | mediumArticleNotifier.setloader(false); 34 | } 35 | }, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "155353198934", 4 | "firebase_url": "https://blog-app-af235.firebaseio.com", 5 | "project_id": "blog-app-af235", 6 | "storage_bucket": "blog-app-af235.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:155353198934:android:d212367223f0c05a9426f7", 12 | "android_client_info": { 13 | "package_name": "tech.himanshusharma.blog_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyD6NPw6HZ737kTps5tqcwV5vYmwf0RTrPE" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "155353198934-hkeu550m5q1ap50anqpq2nq18cb7gl8k.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "155353198934-ljpkbbkq49k0gut7po1ae5d82ledbiuq.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "blogApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/floating_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:provider/provider.dart'; 5 | 6 | class FloatingButton extends StatefulWidget { 7 | const FloatingButton({required this.buttonText, required this.onPressed}); 8 | 9 | final String buttonText; 10 | final Function() onPressed; 11 | @override 12 | _FloatingButtonState createState() => _FloatingButtonState(); 13 | } 14 | 15 | class _FloatingButtonState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | final DarkThemeProvider themeChange = 19 | Provider.of(context); 20 | return Container( 21 | width: MediaQuery.of(context).size.width - 20, 22 | margin: const EdgeInsets.only(bottom: 10), 23 | height: kFloatingActionButtonMargin * 3, 24 | child: ElevatedButton( 25 | style: ElevatedButton.styleFrom( 26 | primary: 27 | themeChange.darkTheme ? Colors.white : AppTheme.primaryColor, 28 | shape: 29 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), 30 | ), 31 | onPressed: widget.onPressed, 32 | child: Text( 33 | widget.buttonText, 34 | style: TextStyle( 35 | color: themeChange.darkTheme 36 | ? AppTheme.primaryColor 37 | : Colors.white, 38 | fontSize: 18), 39 | )), 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | blog_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 13 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: blog_app 2 | description: A new Flutter application. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | after_layout: ^1.1.0 13 | cached_network_image: ^3.1.0+1 14 | firebase_core: ^2.4.1 15 | firebase_database: ^10.0.9 16 | flutter_spinkit: ^5.1.0 17 | google_mobile_ads: ^2.3.0 18 | http: ^0.13.4 19 | provider: ^6.0.1 20 | shared_preferences: ^2.0.8 21 | smooth_page_indicator: ^1.0.0+2 22 | supabase: ^1.2.0 23 | timeago: ^3.1.0 24 | url_launcher: ^6.0.12 25 | webview_flutter: ^4.0.1 26 | firebase_auth: 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | flutter_lints: ^2.0.1 32 | 33 | flutter: 34 | uses-material-design: true 35 | 36 | assets: 37 | - assets/ 38 | fonts: 39 | - family: Nunito 40 | fonts: 41 | - asset: assets/google_fonts/Nunito-ExtraLight.ttf 42 | weight: 200 43 | - asset: assets/google_fonts/Nunito-ExtraLightItalic.ttf 44 | weight: 200 45 | - asset: assets/google_fonts/Nunito-Light.ttf 46 | weight: 300 47 | - asset: assets/google_fonts/Nunito-LightItalic.ttf 48 | weight: 300 49 | - asset: assets/google_fonts/Nunito-Regular.ttf 50 | weight: 400 51 | - asset: assets/google_fonts/Nunito-SemiBold.ttf 52 | weight: 600 53 | - asset: assets/google_fonts/Nunito-SemiBoldItalic.ttf 54 | weight: 600 55 | - asset: assets/google_fonts/Nunito-Bold.ttf 56 | weight: 700 57 | - asset: assets/google_fonts/Nunito-BoldItalic.ttf 58 | weight: 700 59 | - asset: assets/google_fonts/Nunito-ExtraBold.ttf 60 | weight: 800 61 | - asset: assets/google_fonts/Nunito-ExtraBoldItalic.ttf 62 | weight: 800 63 | - asset: assets/google_fonts/Nunito-Black.ttf 64 | weight: 900 65 | - asset: assets/google_fonts/Nunito-BlackItalic.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /lib/widgets/post_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class PostCard extends StatelessWidget { 7 | const PostCard({required this.post, Key? key}) : super(key: key); 8 | final Post post; 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Padding( 13 | padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2.5), 14 | child: InkWell( 15 | onTap: () { 16 | Navigator.pushNamed(context, RouteConstant.VIEW_POST, 17 | arguments: post); 18 | }, 19 | child: Card( 20 | elevation: 4.0, 21 | color: AppTheme.primaryColor, 22 | child: Padding( 23 | padding: const EdgeInsets.all(10), 24 | child: Column( 25 | crossAxisAlignment: CrossAxisAlignment.start, 26 | children: [ 27 | Row( 28 | children: [ 29 | const Icon( 30 | Icons.border_color, 31 | size: 18.0, 32 | color: Colors.white, 33 | ), 34 | const SizedBox( 35 | width: 15, 36 | ), 37 | Text( 38 | post.title, 39 | style: const TextStyle( 40 | color: Colors.white, 41 | fontSize: 20.0, 42 | fontWeight: FontWeight.w800, 43 | ), 44 | ), 45 | ], 46 | ), 47 | const SizedBox( 48 | height: 12, 49 | ), 50 | Text( 51 | post.body, 52 | style: const TextStyle( 53 | color: Colors.white, 54 | ), 55 | ) 56 | ], 57 | ), 58 | )), 59 | ), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: App Release 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | jobs: 7 | version: 8 | name: Create version number 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Fetch all history for all tags and branches 13 | run: | 14 | git fetch --prune --depth=10000 15 | - name: Install GitVersion 16 | uses: gittools/actions/gitversion/setup@v0.9.2 17 | with: 18 | versionSpec: "5.2.x" 19 | - name: Use GitVersion 20 | id: gitversion 21 | uses: gittools/actions/gitversion/execute@v0.9.2 22 | - name: Create version.txt with nuGetVersion 23 | run: echo ${{ steps.gitversion.outputs.nuGetVersion }} > version.txt 24 | - name: Upload version.txt 25 | uses: actions/upload-artifact@v2 26 | with: 27 | name: gitversion 28 | path: version.txt 29 | build: 30 | name: Build APK and Create release 31 | needs: [version] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v1 35 | - uses: actions/setup-java@v1 36 | with: 37 | java-version: "12.x" 38 | - uses: subosito/flutter-action@v1 39 | with: 40 | channel: beta 41 | - name: Get version.txt 42 | uses: actions/download-artifact@v2 43 | with: 44 | name: gitversion 45 | - name: Read version 46 | id: version 47 | uses: juliangruber/read-file-action@v1 48 | with: 49 | path: version.txt 50 | - run: flutter pub get 51 | # - run: flutter test 52 | # We can only build for android becuase we are on linux :D 53 | - run: flutter build apk --release --split-per-abi 54 | - run: flutter build appbundle 55 | - name: Create a Release in GitHub 56 | uses: ncipollo/release-action@v1 57 | with: 58 | artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab" 59 | # Get token to create release from secret (Token needs permission to do so) 60 | token: ${{ secrets.GH_TOKEN }} 61 | tag: ${{ steps.version.outputs.content }} 62 | commit: ${{ github.sha }} 63 | # Mark as pre-release 64 | prerelease: true 65 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 33 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/koptlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "tech.himanshusharma.blog_app" 43 | minSdkVersion 21 44 | targetSdkVersion 31 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 48 | } 49 | 50 | buildTypes { 51 | release { 52 | // TODO: Add your own signing config for the release build. 53 | // Signing with the debug keys for now, so `flutter run --release` works. 54 | signingConfig signingConfigs.debug 55 | } 56 | } 57 | } 58 | 59 | flutter { 60 | source '../..' 61 | } 62 | 63 | dependencies { 64 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 65 | testImplementation 'junit:junit:4.13.1' 66 | androidTestImplementation 'androidx.test:runner:1.3.0' 67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 68 | } -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /lib/helpers/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'colors.dart'; 4 | 5 | final ThemeData darkTheme = ThemeData( 6 | primarySwatch: Colors.grey, 7 | primaryColor: Colors.black, 8 | fontFamily: 'Nunito', 9 | brightness: Brightness.dark, 10 | backgroundColor: const Color(0xFF212121), 11 | colorScheme: ColorScheme.fromSwatch() 12 | .copyWith(secondary: Colors.white, brightness: Brightness.dark), 13 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 14 | foregroundColor: AppTheme.primaryColor, backgroundColor: Colors.white), 15 | dividerColor: Colors.black12, 16 | inputDecorationTheme: const InputDecorationTheme( 17 | // enabledBorder: new OutlineInputBorder( 18 | // borderSide: BorderSide(color: AppTheme.primaryColor ), 19 | // ), 20 | focusedBorder: OutlineInputBorder( 21 | borderSide: BorderSide(color: Colors.white), 22 | ), 23 | ), 24 | iconTheme: const IconThemeData( 25 | color: Colors.white, 26 | ), 27 | appBarTheme: const AppBarTheme( 28 | elevation: 0, 29 | color: Colors.transparent, 30 | centerTitle: true, 31 | toolbarTextStyle: TextStyle(color: Colors.white), 32 | iconTheme: IconThemeData(), 33 | titleTextStyle: TextStyle( 34 | color: Colors.white, 35 | fontFamily: 'Nunito', 36 | fontSize: 20.0, 37 | fontWeight: FontWeight.w700, 38 | ), 39 | )); 40 | 41 | final ThemeData lightTheme = ThemeData( 42 | primarySwatch: Colors.purple, 43 | primaryColor: AppTheme.primaryColor, 44 | fontFamily: 'Nunito', 45 | brightness: Brightness.light, 46 | backgroundColor: const Color(0xFFE5E5E5), 47 | colorScheme: ColorScheme.fromSwatch() 48 | .copyWith(secondary: Colors.white, brightness: Brightness.light), 49 | floatingActionButtonTheme: const FloatingActionButtonThemeData( 50 | foregroundColor: Colors.white, backgroundColor: AppTheme.primaryColor), 51 | dividerColor: Colors.white54, 52 | inputDecorationTheme: const InputDecorationTheme( 53 | focusedBorder: OutlineInputBorder( 54 | borderSide: BorderSide(color: AppTheme.primaryColor), 55 | ), 56 | ), 57 | iconTheme: const IconThemeData( 58 | color: AppTheme.primaryColor, 59 | ), 60 | appBarTheme: const AppBarTheme( 61 | elevation: 0, 62 | centerTitle: true, 63 | color: Colors.transparent, 64 | iconTheme: IconThemeData( 65 | color: AppTheme.primaryColor, 66 | ), 67 | titleTextStyle: TextStyle( 68 | color: AppTheme.primaryColor, 69 | fontFamily: 'Nunito', 70 | fontSize: 20.0, 71 | fontWeight: FontWeight.w700, 72 | ))); 73 | -------------------------------------------------------------------------------- /lib/views/drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/colors.dart'; 2 | import 'package:blog_app/providers/theme_notifier.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:provider/provider.dart'; 7 | 8 | class BlogDrawer extends StatefulWidget { 9 | @override 10 | _BlogDrawerState createState() => _BlogDrawerState(); 11 | } 12 | 13 | class _BlogDrawerState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | final DarkThemeProvider themeChange = 17 | Provider.of(context); 18 | bool swithValue = themeChange.darkTheme; 19 | final double height = MediaQuery.of(context).size.height; 20 | return Drawer( 21 | child: ListView( 22 | children: [ 23 | Image.asset( 24 | themeChange.darkTheme 25 | ? 'assets/blog_flutter_dark.png' 26 | : 'assets/blog_flutter_light.png', 27 | fit: BoxFit.cover, 28 | height: height * 0.15, 29 | ), 30 | Ink( 31 | child: ListTile( 32 | title: const Text('About', 33 | style: TextStyle( 34 | fontSize: 15.0, 35 | // color: Colors.black, 36 | fontWeight: FontWeight.w600, 37 | )), 38 | trailing: const Icon( 39 | Icons.info, 40 | color: Colors.blueAccent, 41 | ), 42 | onTap: () { 43 | Navigator.pushNamed(context, RouteConstant.ABOUT); 44 | }, 45 | ), 46 | ), 47 | Ink( 48 | child: ListTile( 49 | title: const Text('Dark Mode', 50 | style: TextStyle( 51 | fontSize: 15.0, 52 | fontWeight: FontWeight.w600, 53 | )), 54 | trailing: Transform.scale( 55 | scale: 0.7, 56 | origin: const Offset(25, 0), 57 | child: CupertinoSwitch( 58 | activeColor: AppTheme.primaryColor, 59 | value: swithValue, 60 | onChanged: (bool value) { 61 | setState(() { 62 | swithValue = !swithValue; 63 | themeChange.darkTheme = swithValue; 64 | }); 65 | }, 66 | ), 67 | ), 68 | onTap: () { 69 | setState(() { 70 | swithValue = !swithValue; 71 | themeChange.darkTheme = swithValue; 72 | }); 73 | }, 74 | ), 75 | ), 76 | ], 77 | ), 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /v1.0/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: null 10 | tags: 11 | --- 12 | 13 | 14 | 15 | ## Getting Started 16 | 17 | Learn how to use DeveloperHub using our step-by-step guide: 18 | 19 | Click here and start editing your documentation. 20 | 21 | `{{page.vars.PRODUCT_NAME}}` 22 | 23 | ### Formatting 24 | 25 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 26 | 27 | ### Markdown 28 | 29 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 30 | 31 | ### Linking Pages 32 | 33 | To reference other pages in your documentation, use `@` to start linking. 34 | 35 | ### Blocks 36 | 37 | There are many in-page blocks that you can try which will make your documentation richer, just type 38 | 39 | . Go have a look! 👇 40 | 41 | 42 | #### Code 43 | 44 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 45 | 46 | 47 | {% code %} 48 | {% tab language="none" %} 49 | 50 | {% /tab %} 51 | {% /code %} 52 | 53 | 54 | 55 | 56 | 57 | console.log("Hello World"); 58 | 59 | 60 | 61 | print("Hello World!") 62 | 63 | 64 | 65 | package main 66 | 67 | import "fmt" 68 | 69 | func main() { fmt.Println("hello world") } 70 | 71 | 72 | {% code %} 73 | {% tab language="none" %} 74 | console.log("try this") 75 | {% /tab %} 76 | {% /code %} 77 | 78 | 79 | 80 | 81 | #### Images & Videos 82 | 83 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 84 | 85 | ### Sidebar 86 | 87 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 88 | 89 | ### Logo and Colour 90 | 91 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 92 | 93 | ### Publishing Documentation 94 | 95 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 96 | 97 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 98 | 99 | ### Need More Help? 100 | 101 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/app.flx 96 | **/ios/Flutter/app.zip 97 | **/ios/Flutter/flutter_assets/ 98 | **/ios/Flutter/flutter_export_environment.sh 99 | **/ios/ServiceDefinitions.json 100 | **/ios/Runner/GeneratedPluginRegistrant.* 101 | 102 | # macOS 103 | **/macos/Flutter/GeneratedPluginRegistrant.swift 104 | **/macos/Flutter/Flutter-Debug.xcconfig 105 | **/macos/Flutter/Flutter-Release.xcconfig 106 | **/macos/Flutter/Flutter-Profile.xcconfig 107 | 108 | # Coverage 109 | coverage/ 110 | 111 | # Symbols 112 | app.*.symbols 113 | 114 | # Exceptions to above rules. 115 | !**/ios/**/default.mode1v3 116 | !**/ios/**/default.mode2v3 117 | !**/ios/**/default.pbxuser 118 | !**/ios/**/default.perspectivev3 119 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 120 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/views/post/view_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/post.dart'; 2 | import 'package:blog_app/routes/route_constants.dart'; 3 | import 'package:blog_app/services/post_service.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:timeago/timeago.dart' as timeago; 6 | 7 | class PostView extends StatefulWidget { 8 | const PostView(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _PostViewState createState() => _PostViewState(); 14 | } 15 | 16 | class _PostViewState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text( 22 | widget.post.title, 23 | ), 24 | leading: IconButton( 25 | icon: const Icon( 26 | Icons.arrow_back_ios, 27 | ), 28 | onPressed: () { 29 | Navigator.pop(context); 30 | }, 31 | ), 32 | ), 33 | // backgroundColor: Color(0xffc8d9ff), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8.0, right: 8.0), 36 | child: SingleChildScrollView( 37 | child: Column( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.all(8.0), 41 | child: SizedBox( 42 | width: MediaQuery.of(context).size.width, 43 | child: Card( 44 | child: Padding( 45 | padding: const EdgeInsets.all(8.0), 46 | child: Text( 47 | widget.post.body, 48 | style: const TextStyle(fontSize: 16.0), 49 | ), 50 | )), 51 | ), 52 | ), 53 | const Divider(), 54 | Row( 55 | children: [ 56 | Expanded( 57 | child: Padding( 58 | padding: const EdgeInsets.fromLTRB(12, 4, 4, 4), 59 | child: Text( 60 | 'Published:${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.post.date))}', 61 | style: const TextStyle( 62 | fontSize: 14.0, 63 | // color: Color(0xff133337), 64 | ), 65 | ), 66 | ), 67 | ), 68 | IconButton( 69 | icon: const Icon(Icons.delete), 70 | onPressed: () async { 71 | PostService().deletePost(widget.post); 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ], 76 | ), 77 | ], 78 | ), 79 | ), 80 | ), 81 | floatingActionButton: FloatingActionButton( 82 | onPressed: () { 83 | Navigator.pushNamed(context, RouteConstant.EDIT_POST, 84 | arguments: widget.post); 85 | }, 86 | child: const Icon(Icons.edit), 87 | ), 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/views/post/add_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/floating_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../models/post.dart'; 5 | import '../../services/post_service.dart'; 6 | 7 | class AddPost extends StatefulWidget { 8 | @override 9 | _AddPostState createState() => _AddPostState(); 10 | } 11 | 12 | class _AddPostState extends State { 13 | TextEditingController titleEditingController = TextEditingController(); 14 | TextEditingController bodyEditingController = TextEditingController(); 15 | final GlobalKey _formkey = GlobalKey(); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | leading: IconButton( 22 | icon: const Icon( 23 | Icons.arrow_back_ios, 24 | ), 25 | onPressed: () { 26 | Navigator.pop(context); 27 | }, 28 | ), 29 | title: const Text( 30 | 'Add Post', 31 | ), 32 | ), 33 | body: Form( 34 | key: _formkey, 35 | child: Padding( 36 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 37 | child: Column( 38 | children: [ 39 | TextFormField( 40 | controller: titleEditingController, 41 | decoration: const InputDecoration( 42 | labelText: 'Post Title', 43 | border: OutlineInputBorder(), 44 | contentPadding: EdgeInsets.only(right: 15, left: 15), 45 | ), 46 | validator: (String? val) { 47 | if (val!.isEmpty) { 48 | return "Title filed can't be empty"; 49 | } 50 | }, 51 | ), 52 | const SizedBox( 53 | height: 15, 54 | ), 55 | TextFormField( 56 | controller: bodyEditingController, 57 | decoration: const InputDecoration( 58 | labelText: 'Post Body', 59 | border: OutlineInputBorder(), 60 | contentPadding: EdgeInsets.only( 61 | right: 15, top: 15, bottom: 50, left: 15), 62 | ), 63 | maxLines: 7, 64 | validator: (String? val) { 65 | if (val!.isEmpty) { 66 | return "Body field can't be empty"; 67 | } 68 | }, 69 | ) 70 | ], 71 | ), 72 | ), 73 | ), 74 | floatingActionButton: FloatingButton( 75 | buttonText: 'Add The Post', 76 | onPressed: () { 77 | addPost(); 78 | }), 79 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 80 | ); 81 | } 82 | 83 | void addPost() { 84 | debugPrint('addPost form validation:${_formkey.currentState!.validate()}'); 85 | if (_formkey.currentState!.validate()) { 86 | _formkey.currentState!.save(); 87 | final Post post = Post( 88 | title: titleEditingController.text, 89 | body: bodyEditingController.text, 90 | date: DateTime.now().millisecondsSinceEpoch); 91 | debugPrint('addPost${post.toString}'); 92 | PostService().addPost(post); 93 | _formkey.currentState!.reset(); 94 | Navigator.pop(context); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@himanshusharma.tech. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/routes/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/views/about.dart'; 3 | import 'package:blog_app/views/home.dart'; 4 | import 'package:blog_app/views/medium/medium_articles.dart'; 5 | import 'package:blog_app/views/medium/medium_articles_webview.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import '../models/post.dart'; 9 | import '../views/post/add_post.dart'; 10 | import '../views/post/edit_post.dart'; 11 | import '../views/post/view_post.dart'; 12 | import '../views/undefined_route.dart'; 13 | 14 | class RoutePage { 15 | static Route generateRoute(RouteSettings settings) { 16 | switch (settings.name) { 17 | case RouteConstant.ROOT: 18 | return PageRouteBuilder( 19 | settings: settings, 20 | pageBuilder: (_, __, ___) => HomePage(), 21 | transitionsBuilder: (_, Animation a, __, Widget c) => 22 | FadeTransition(opacity: a, child: c)); 23 | 24 | case RouteConstant.ADD_POST: 25 | return PageRouteBuilder( 26 | settings: settings, 27 | pageBuilder: (_, __, ___) => AddPost(), 28 | transitionsBuilder: (_, Animation a, __, Widget c) => 29 | FadeTransition(opacity: a, child: c)); 30 | 31 | case RouteConstant.EDIT_POST: 32 | final Post post = settings.arguments as Post; 33 | return PageRouteBuilder( 34 | settings: settings, 35 | pageBuilder: (_, __, ___) => EditPost(post), 36 | transitionsBuilder: (_, Animation a, __, Widget c) => 37 | FadeTransition(opacity: a, child: c)); 38 | 39 | case RouteConstant.VIEW_POST: 40 | final Post post = settings.arguments as Post; 41 | return PageRouteBuilder( 42 | settings: settings, 43 | pageBuilder: (_, __, ___) => PostView(post), 44 | transitionsBuilder: (_, Animation a, __, Widget c) => 45 | FadeTransition(opacity: a, child: c)); 46 | 47 | case RouteConstant.ABOUT: 48 | return PageRouteBuilder( 49 | settings: settings, 50 | pageBuilder: (_, __, ___) => About(), 51 | transitionsBuilder: (_, Animation a, __, Widget c) => 52 | FadeTransition(opacity: a, child: c)); 53 | // return MaterialPageRoute(builder: (_) => About()); 54 | 55 | case RouteConstant.MEDIUM_ARTICLES: 56 | return PageRouteBuilder( 57 | settings: settings, 58 | pageBuilder: (_, __, ___) => MediumArticles(), 59 | transitionsBuilder: (_, Animation a, __, Widget c) => 60 | FadeTransition(opacity: a, child: c)); 61 | 62 | case RouteConstant.MEDIUM_ARTICLES_WEB_VIEW: 63 | final Map arguments = 64 | settings.arguments as Map; 65 | return PageRouteBuilder( 66 | settings: settings, 67 | pageBuilder: (_, __, ___) => MediumArticlesWebView( 68 | title: arguments['title']!, url: arguments['url']!), 69 | transitionsBuilder: (_, Animation a, __, Widget c) => 70 | FadeTransition(opacity: a, child: c)); 71 | 72 | default: 73 | return PageRouteBuilder( 74 | settings: settings, 75 | pageBuilder: (_, __, ___) => UndefinedView( 76 | routeName: settings.name!, 77 | ), 78 | transitionsBuilder: (_, Animation a, __, Widget c) => 79 | FadeTransition(opacity: a, child: c)); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "shubham-chhimpa", 10 | "name": "Shubham Chhimpa", 11 | "avatar_url": "https://avatars0.githubusercontent.com/u/38981756?v=4", 12 | "profile": "https://www.linkedin.com/in/shubhamchhimpa/", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "carlosfrodrigues", 19 | "name": "Carlos Felix", 20 | "avatar_url": "https://avatars3.githubusercontent.com/u/18339454?v=4", 21 | "profile": "http://carlosfelix.pythonanywhere.com/", 22 | "contributions": [ 23 | "design" 24 | ] 25 | }, 26 | { 27 | "login": "derangga", 28 | "name": "Dimas Rangga", 29 | "avatar_url": "https://avatars2.githubusercontent.com/u/31648630?v=4", 30 | "profile": "https://medium.com/@derangga", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "arbazdiwan", 37 | "name": "Arbaz Mustufa Diwan", 38 | "avatar_url": "https://avatars3.githubusercontent.com/u/24837320?v=4", 39 | "profile": "https://github.com/arbazdiwan", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "Mrgove10", 46 | "name": "Adrien", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/25491408?v=4", 48 | "profile": "http://www.adrienrichard.com/", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "Wizpna", 55 | "name": "Promise Amadi", 56 | "avatar_url": "https://avatars2.githubusercontent.com/u/15036164?v=4", 57 | "profile": "https://promise.hashnode.dev/", 58 | "contributions": [ 59 | "design" 60 | ] 61 | }, 62 | { 63 | "login": "daruanugerah", 64 | "name": "Daru Anugerah Setiawan", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/20470960?v=4", 66 | "profile": "https://linkedin.com/in/daruanugerah", 67 | "contributions": [ 68 | "design" 69 | ] 70 | }, 71 | { 72 | "login": "yash2189", 73 | "name": "Yash Ajgaonkar", 74 | "avatar_url": "https://avatars2.githubusercontent.com/u/31548778?v=4", 75 | "profile": "https://www.linkedin.com/in/yash-ajgaonkar-289520168/?", 76 | "contributions": [ 77 | "doc" 78 | ] 79 | }, 80 | { 81 | "login": "Dhruv-Sachdev1313", 82 | "name": "Dhruv Sachdev", 83 | "avatar_url": "https://avatars0.githubusercontent.com/u/56223242?v=4", 84 | "profile": "https://github.com/Dhruv-Sachdev1313", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "Janhavi23", 91 | "name": "Janhavi", 92 | "avatar_url": "https://avatars3.githubusercontent.com/u/56731465?v=4", 93 | "profile": "https://github.com/Janhavi23", 94 | "contributions": [ 95 | "code", 96 | "design" 97 | ] 98 | }, 99 | { 100 | "login": "Saransh-cpp", 101 | "name": "Saransh Chopra", 102 | "avatar_url": "https://avatars.githubusercontent.com/u/74055102?v=4", 103 | "profile": "https://github.com/Saransh-cpp", 104 | "contributions": [ 105 | "design", 106 | "doc" 107 | ] 108 | } 109 | ], 110 | "contributorsPerLine": 7, 111 | "projectName": "Flutter-Blog-App", 112 | "projectOwner": "himanshusharma89", 113 | "repoType": "github", 114 | "repoHost": "https://github.com", 115 | "skipCi": true 116 | } 117 | -------------------------------------------------------------------------------- /v1.0-copy/himanshu/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | type: page 3 | title: Getting Started 4 | listed: true 5 | slug: getting-started 6 | description: 7 | index_title: Getting Started 8 | hidden: 9 | keywords: 10 | tags: 11 | --- 12 | 13 | Learn how to use DeveloperHub using our step-by-step guide: 14 | 15 | {% html %} 16 | 17 | 18 | 24 | 25 | {% /html %} 26 | 27 | Click here and start editing your documentation. 28 | 29 | ## Formatting 30 | 31 | You can highlight sentences and format them on the go (bold, italics, headings and so). You can also use the usual keyboard shortcuts: Ctrl/⌘+B, Ctrl/⌘+I. Hit Ctrl/⌘+/ to see all possible combinations. 32 | 33 | {% image url="https://uploads.developerhub.io/prod/02/r09kzcr7u2vusptm6uj2itmv7tg7wu32fs07xdmezswz6zqwv01a31vn98pmvrd2.png" mode="responsive" height="182" width="930" %} 34 | {% /image %} 35 | 36 | ## Markdown 37 | 38 | You can write markdown directly, and we'll transform it right away to what you are used to! 🚀 39 | 40 | ## Linking Pages 41 | 42 | To reference other pages in your documentation, use `@` to start linking. 43 | 44 | ## Blocks 45 | 46 | There are many in-page blocks that you can try which will make your documentation richer, just type {% key key=" /" /%}. Go have a look! 👇 47 | 48 | {% image url="https://uploads.developerhub.io/prod/02/ek4dchom06zasu70pblhjmnn80svgue7v9mws01hir8t8bgcy26148e2ou9dkhcq.png" mode="responsive" height="914" width="540" %} 49 | {% /image %} 50 | 51 | ### Code 52 | 53 | You can write code by inserting the code block plugin either manually or by using markdown's syntax for fenced code block. 54 | 55 | {% code %} 56 | {% tab language="javascript" %} 57 | console.log("Hello World"); 58 | {% /tab %} 59 | {% tab language="python" %} 60 | print("Hello World!") 61 | {% /tab %} 62 | {% tab language="go" %} 63 | package main 64 | 65 | import "fmt" 66 | 67 | func main() { 68 | fmt.Println("hello world") 69 | } 70 | {% /tab %} 71 | {% /code %} 72 | 73 | ### Images & Videos 74 | 75 | Do you see all the images here? They were uploaded by using the plugin! You can also embed YouTube videos. 76 | 77 | ## Sidebar 78 | 79 | To your 👈, you will find the sidebar where you can set up your project and account settings, as well to add more versions and documentation. 80 | 81 | ## Logo and Colour 82 | 83 | Up there 👆, you can add navigation links, change the logo, favicon, as well as the main colour of your documentation (make sure it is colourful enough 🌈). 84 | 85 | ## Publishing Documentation 86 | 87 | You can publish the version of the documentation and all the documentation that belong to it from the sidebar on the left 👈. Just choose the version and click on "Publish to Public". It will be immediately available 🚀. 88 | 89 | Then, you can view your published documentation by going to your subdomain [himanshu.developerhub.io](https://himanshu.developerhub.io), or by the shortcuts in the sidebar. 90 | 91 | ## Need More Help? 92 | 93 | You can talk to us directly through "Let's Talk" button in the bottom left, or see the documentation at [docs.developerhub.io](https://docs.developerhub.io). 94 | 95 | TESTING IN PROGRESS -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 82 | install! 'cocoapods', :disable_input_output_paths => true 83 | 84 | post_install do |installer| 85 | installer.pods_project.targets.each do |target| 86 | target.build_configurations.each do |config| 87 | config.build_settings['ENABLE_BITCODE'] = 'NO' 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/views/post/edit_post.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/routes/route_constants.dart'; 2 | import 'package:blog_app/services/post_service.dart'; 3 | import 'package:blog_app/widgets/floating_button.dart'; 4 | import 'package:blog_app/models/post.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class EditPost extends StatefulWidget { 8 | const EditPost(this.post); 9 | 10 | final Post post; 11 | 12 | @override 13 | _EditPostState createState() => _EditPostState(); 14 | } 15 | 16 | class _EditPostState extends State { 17 | late TextEditingController titleEditingController; 18 | late TextEditingController bodyEditingController; 19 | final GlobalKey _formkey = GlobalKey(); 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | titleEditingController = TextEditingController(text: widget.post.title); 25 | bodyEditingController = TextEditingController(text: widget.post.body); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: const Text( 33 | 'Edit Post', 34 | ), 35 | leading: IconButton( 36 | icon: const Icon( 37 | Icons.arrow_back_ios, 38 | ), 39 | onPressed: () { 40 | Navigator.pop(context); 41 | }, 42 | ), 43 | ), 44 | body: Form( 45 | key: _formkey, 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 15.0, left: 8.0, right: 8.0), 48 | child: Column( 49 | children: [ 50 | TextFormField( 51 | controller: titleEditingController, 52 | decoration: const InputDecoration( 53 | filled: true, 54 | labelText: 'Post Title', 55 | border: OutlineInputBorder()), 56 | validator: (String? val) { 57 | if (val!.isEmpty) { 58 | return "Title filed can't be empty"; 59 | } 60 | }, 61 | ), 62 | const SizedBox( 63 | height: 15, 64 | ), 65 | TextFormField( 66 | controller: bodyEditingController, 67 | decoration: const InputDecoration( 68 | filled: true, 69 | labelText: 'Post Body', 70 | border: OutlineInputBorder()), 71 | maxLines: 10, 72 | validator: (String? val) { 73 | if (val!.isEmpty) { 74 | return "Body feild can't be empty"; 75 | } 76 | }, 77 | ) 78 | ], 79 | ), 80 | ), 81 | ), 82 | floatingActionButton: FloatingButton( 83 | buttonText: 'Save Changes', 84 | onPressed: () { 85 | updatePost(); 86 | }), 87 | floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, 88 | ); 89 | } 90 | 91 | void updatePost() { 92 | debugPrint( 93 | 'updatePost form validation:${_formkey.currentState!.validate()}'); 94 | if (_formkey.currentState!.validate()) { 95 | _formkey.currentState!.save(); 96 | final Post post = Post( 97 | key: widget.post.key, 98 | title: titleEditingController.text, 99 | body: bodyEditingController.text, 100 | date: DateTime.now().millisecondsSinceEpoch); 101 | PostService().updatePost(post); 102 | _formkey.currentState!.reset(); 103 | Navigator.pushReplacementNamed(context, RouteConstant.ROOT); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/helpers/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/widgets/page_view.dart'; 2 | 3 | List> contributors = const >[ 4 | { 5 | 'login': 'shubham-chhimpa', 6 | 'name': 'Shubham Chhimpa', 7 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/38981756?v=4', 8 | 'profile': 'https://www.linkedin.com/in/shubhamchhimpa/', 9 | 'contributions': ['code'] 10 | }, 11 | { 12 | 'login': 'carlosfrodrigues', 13 | 'name': 'Carlos Felix', 14 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/18339454?v=4', 15 | 'profile': 'http://carlosfelix.pythonanywhere.com/', 16 | 'contributions': ['design'] 17 | }, 18 | { 19 | 'login': 'derangga', 20 | 'name': 'Dimas Rangga', 21 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31648630?v=4', 22 | 'profile': 'https://medium.com/@derangga', 23 | 'contributions': ['code'] 24 | }, 25 | { 26 | 'login': 'arbazdiwan', 27 | 'name': 'Arbaz Mustufa Diwan', 28 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/24837320?v=4', 29 | 'profile': 'https://github.com/arbazdiwan', 30 | 'contributions': ['code'] 31 | }, 32 | { 33 | 'login': 'Mrgove10', 34 | 'name': 'Adrien', 35 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/25491408?v=4', 36 | 'profile': 'http://www.adrienrichard.com/', 37 | 'contributions': ['code'] 38 | }, 39 | { 40 | 'login': 'Wizpna', 41 | 'name': 'Promise Amadi', 42 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/15036164?v=4', 43 | 'profile': 'https://promise.hashnode.dev/', 44 | 'contributions': ['design'] 45 | }, 46 | { 47 | 'login': 'daruanugerah', 48 | 'name': 'Daru Anugerah Setiawan', 49 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/20470960?v=4', 50 | 'profile': 'https://linkedin.com/in/daruanugerah', 51 | 'contributions': ['design'] 52 | }, 53 | { 54 | 'login': 'yash2189', 55 | 'name': 'Yash Ajgaonkar', 56 | 'avatar_url': 'https://avatars2.githubusercontent.com/u/31548778?v=4', 57 | 'profile': 'https://www.linkedin.com/in/yash-ajgaonkar-289520168/?', 58 | 'contributions': ['doc'] 59 | }, 60 | { 61 | 'login': 'Dhruv-Sachdev1313', 62 | 'name': 'Dhruv Sachdev', 63 | 'avatar_url': 'https://avatars0.githubusercontent.com/u/56223242?v=4', 64 | 'profile': 'https://github.com/Dhruv-Sachdev1313', 65 | 'contributions': ['code'] 66 | }, 67 | { 68 | 'login': 'Janhavi23', 69 | 'name': 'Janhavi', 70 | 'avatar_url': 'https://avatars3.githubusercontent.com/u/56731465?v=4', 71 | 'profile': 'https://github.com/Janhavi23', 72 | 'contributions': ['code', 'design'] 73 | }, 74 | { 75 | 'login': 'Saransh-cpp', 76 | 'name': 'Saransh Chopra', 77 | 'avatar_url': 'https://avatars.githubusercontent.com/u/74055102?v=4', 78 | 'profile': 'https://github.com/Saransh-cpp', 79 | 'contributions': ['design', 'doc'] 80 | } 81 | ]; 82 | 83 | /// SOCIAL LINKS 84 | 85 | List> social = const >[ 86 | { 87 | 'URL': 'https://github.com/himanshusharma89', 88 | 'iconURL': 'https://img.icons8.com/fluent/50/000000/github.png' 89 | }, 90 | { 91 | 'URL': 'https://twitter.com/_SharmaHimanshu', 92 | 'iconURL': 'https://img.icons8.com/color/48/000000/twitter.png' 93 | }, 94 | { 95 | 'URL': 'https://www.linkedin.com/in/himanshusharma89/', 96 | 'iconURL': 'https://img.icons8.com/color/48/000000/linkedin.png' 97 | }, 98 | ]; 99 | 100 | List introSlider = const [ 101 | PageViewWidget( 102 | text: 'Do you have ideas that you want to pen down?', 103 | image: 'Blog3.png', 104 | ), 105 | PageViewWidget( 106 | text: 'Looking for a spot to write blogs?', 107 | image: 'Blog2.png', 108 | ), 109 | PageViewWidget( 110 | text: 111 | 'You came to the right place!\nWrite, read and even fetch articles from internet!', 112 | image: 'Blog1.jpg', 113 | ), 114 | ]; 115 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/launcher.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/router.dart'; 5 | import 'package:blog_app/services/auth_service.dart'; 6 | import 'package:blog_app/services/shared_preference_service.dart'; 7 | import 'package:blog_app/views/home.dart'; 8 | import 'package:blog_app/views/intro_slider.dart'; 9 | import 'package:firebase_core/firebase_core.dart'; 10 | import 'package:flutter/foundation.dart'; 11 | import 'package:flutter/material.dart'; 12 | import 'package:flutter/services.dart'; 13 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 14 | import 'package:provider/provider.dart'; 15 | 16 | import 'helpers/theme.dart'; 17 | 18 | final Launcher launcher = Launcher(); 19 | 20 | Future main() async { 21 | LicenseRegistry.addLicense(() async* { 22 | final String license = 23 | await rootBundle.loadString('assets/google_fonts/OFL.txt'); 24 | yield LicenseEntryWithLineBreaks(['google_fonts'], license); 25 | }); 26 | WidgetsFlutterBinding.ensureInitialized(); 27 | await Firebase.initializeApp(); 28 | await SharedPreferencesService().init(); 29 | await MobileAds.instance.initialize(); 30 | runApp( 31 | MultiProvider( 32 | providers: >[ 33 | ChangeNotifierProvider( 34 | create: (_) => MediumArticleNotifier()), 35 | ], 36 | child: BlogApp(), 37 | ), 38 | ); 39 | } 40 | 41 | class BlogApp extends StatefulWidget { 42 | @override 43 | _BlogAppState createState() => _BlogAppState(); 44 | } 45 | 46 | class _BlogAppState extends State { 47 | DarkThemeProvider themeChangeProvider = DarkThemeProvider(); 48 | AuthService _authService = AuthService(); 49 | late Widget homeWidget; 50 | late bool signedIn; 51 | 52 | @override 53 | void initState() { 54 | super.initState(); 55 | getCurrentAppTheme(); 56 | checkFirstSeen(); 57 | signInAnonymously(); 58 | } 59 | 60 | void getCurrentAppTheme() { 61 | themeChangeProvider.darkTheme = SharedPreferencesService.getDarkTheme(); 62 | } 63 | 64 | void checkFirstSeen() { 65 | final bool _firstLaunch = SharedPreferencesService.getFirstLaunch(); 66 | 67 | if (_firstLaunch) { 68 | homeWidget = const IntroScreen(); 69 | } else { 70 | homeWidget = HomePage(); 71 | } 72 | SharedPreferencesService.setFirstLaunch(to: false); 73 | setState(() {}); 74 | } 75 | 76 | void signInAnonymously() async { 77 | signedIn = await _authService.signInAnonymously(); 78 | setState(() {}); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | return ChangeNotifierProvider( 84 | create: (_) { 85 | return themeChangeProvider; 86 | }, 87 | child: Consumer( 88 | builder: 89 | (BuildContext context, DarkThemeProvider value, Widget? child) { 90 | return GestureDetector( 91 | onTap: () => hideKeyboard(context), 92 | child: MaterialApp( 93 | debugShowCheckedModeBanner: false, 94 | builder: (_, Widget? child) => 95 | ScrollConfiguration(behavior: MyBehavior(), child: child!), 96 | theme: themeChangeProvider.darkTheme ? darkTheme : lightTheme, 97 | home: homeWidget, 98 | onGenerateRoute: RoutePage.generateRoute), 99 | ); 100 | }, 101 | ), 102 | ); 103 | } 104 | 105 | void hideKeyboard(BuildContext context) { 106 | final FocusScopeNode currentFocus = FocusScope.of(context); 107 | if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) { 108 | FocusManager.instance.primaryFocus!.unfocus(); 109 | } 110 | } 111 | } 112 | 113 | class MyBehavior extends ScrollBehavior { 114 | @override 115 | Widget buildViewportChrome( 116 | BuildContext context, Widget child, AxisDirection axisDirection) { 117 | return child; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /assets/google_fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 The Nunito Project Authors (https://github.com/googlefonts/NunitoFont) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/blog_flutter_dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /assets/blog_flutter_light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PRIVACY_POLICY.md: -------------------------------------------------------------------------------- 1 | **Privacy Policy** 2 | 3 | Himanshu Sharma built the Blog App app as an Open Source app. This SERVICE is provided by Himanshu Sharma at no cost and is intended for use as is. 4 | 5 | This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service. 6 | 7 | If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy. 8 | 9 | The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which are accessible at Blog App unless otherwise defined in this Privacy Policy. 10 | 11 | **Information Collection and Use** 12 | 13 | For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Public comments or posts shared by user.. The information that I request will be retained on your device and is not collected by me in any way. 14 | 15 | The app does use third-party services that may collect information used to identify you. 16 | 17 | Link to the privacy policy of third-party service providers used by the app 18 | 19 | * [Google Play Services](https://www.google.com/policies/privacy/) 20 | * [AdMob](https://support.google.com/admob/answer/6128543?hl=en) 21 | 22 | **Log Data** 23 | 24 | I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third-party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics. 25 | 26 | **Cookies** 27 | 28 | Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory. 29 | 30 | This Service does not use these “cookies” explicitly. However, the app may use third-party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service. 31 | 32 | **Service Providers** 33 | 34 | I may employ third-party companies and individuals due to the following reasons: 35 | 36 | * To facilitate our Service; 37 | * To provide the Service on our behalf; 38 | * To perform Service-related services; or 39 | * To assist us in analyzing how our Service is used. 40 | 41 | I want to inform users of this Service that these third parties have access to their Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose. 42 | 43 | **Security** 44 | 45 | I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security. 46 | 47 | **Links to Other Sites** 48 | 49 | This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services. 50 | 51 | **Children’s Privacy** 52 | 53 | These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13 years of age. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do the necessary actions. 54 | 55 | **Changes to This Privacy Policy** 56 | 57 | I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. 58 | 59 | This policy is effective as of 2022-03-12 60 | 61 | **Contact Us** 62 | 63 | If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at contact@himanshusharma.tech. 64 | 65 | This privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.nisrulz.com/) 66 | -------------------------------------------------------------------------------- /lib/views/intro_slider.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/views/home.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:smooth_page_indicator/smooth_page_indicator.dart'; 5 | class IntroScreen extends StatefulWidget { 6 | const IntroScreen({Key? key}) : super(key: key); 7 | 8 | @override 9 | _IntroScreenState createState() => _IntroScreenState(); 10 | } 11 | 12 | class _IntroScreenState extends State { 13 | late PageController _pageController; 14 | 15 | int _currentPage = 0; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | _pageController = PageController(); 21 | _pageController.addListener(() { 22 | if (_currentPage != _pageController.page!.round()) { 23 | setState(() { 24 | _currentPage = _pageController.page!.round(); 25 | }); 26 | } 27 | }); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _pageController.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return SafeArea( 39 | child: Material( 40 | color: Colors.white, 41 | child: Stack( 42 | children: [ 43 | Positioned.fill( 44 | top: 40, 45 | bottom: 49, 46 | child: SizedBox( 47 | height: 200, 48 | child: PageView( 49 | controller: _pageController, 50 | children: introSlider, 51 | ), 52 | ), 53 | ), 54 | _appBar(), 55 | bottomNavigation(context) 56 | ], 57 | ), 58 | ), 59 | ); 60 | } 61 | 62 | Align bottomNavigation(BuildContext context) { 63 | return Align( 64 | alignment: Alignment.bottomCenter, 65 | child: Row( 66 | children: [ 67 | const SizedBox(width: 15.0), 68 | SmoothPageIndicator( 69 | controller: _pageController, 70 | count: 3, 71 | effect: const WormEffect( 72 | activeDotColor: Colors.blue, 73 | dotHeight: 12.0, 74 | dotWidth: 12.0, 75 | ), 76 | ), 77 | const Spacer(), 78 | AnimatedSwitcher( 79 | duration: const Duration(milliseconds: 200), 80 | child: _currentPage != 2 81 | ? SizedBox( 82 | width: 90, 83 | height: 49, 84 | child: IconButton( 85 | splashColor: Colors.transparent, 86 | padding: const EdgeInsets.all(0.0), 87 | onPressed: () { 88 | _pageController.nextPage( 89 | duration: const Duration(milliseconds: 300), 90 | curve: Curves.linear, 91 | ); 92 | }, 93 | icon: const Icon(Icons.arrow_forward_ios_rounded), 94 | ), 95 | ) 96 | : InkWell( 97 | onTap: () => Navigator.pushReplacement( 98 | context, 99 | PageRouteBuilder( 100 | pageBuilder: (_, __, ___) => HomePage(), 101 | transitionsBuilder: 102 | (_, Animation anim, __, Widget child) => 103 | FadeTransition(opacity: anim, child: child), 104 | transitionDuration: const Duration(milliseconds: 1000), 105 | ), 106 | ), 107 | child: Container( 108 | decoration: const BoxDecoration( 109 | color: Colors.blue, 110 | borderRadius: 111 | BorderRadius.only(topLeft: Radius.circular(15.0)), 112 | ), 113 | width: 90, 114 | height: 49, 115 | child: const Center( 116 | child: Text( 117 | 'START', 118 | style: TextStyle( 119 | fontSize: 16, 120 | fontWeight: FontWeight.bold, 121 | color: Colors.white, 122 | ), 123 | ), 124 | ), 125 | ), 126 | ), 127 | ) 128 | ], 129 | ), 130 | ); 131 | } 132 | 133 | Align _appBar() { 134 | return Align( 135 | alignment: Alignment.topCenter, 136 | child: Padding( 137 | padding: const EdgeInsets.only(top: 3.0), 138 | child: Row( 139 | mainAxisAlignment: MainAxisAlignment.center, 140 | children: [ 141 | const SizedBox(width: 10), 142 | Image.asset( 143 | 'assets/blog_flutter_light.png', 144 | width: 100, 145 | height: 100, 146 | ), 147 | ], 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/views/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/ad_helper.dart'; 2 | import 'package:blog_app/models/post.dart'; 3 | import 'package:blog_app/routes/route_constants.dart'; 4 | import 'package:blog_app/widgets/post_card.dart'; 5 | import 'package:firebase_database/firebase_database.dart'; 6 | import 'package:firebase_database/ui/firebase_animated_list.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:google_mobile_ads/google_mobile_ads.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import '../models/post.dart'; 13 | import '../providers/theme_notifier.dart'; 14 | import 'drawer.dart'; 15 | 16 | class HomePage extends StatefulWidget { 17 | @override 18 | _HomePageState createState() => _HomePageState(); 19 | } 20 | 21 | class _HomePageState extends State { 22 | final FirebaseDatabase _database = FirebaseDatabase.instance; 23 | String nodeName = 'posts'; 24 | List postsList = []; 25 | final GlobalKey _globalKey = GlobalKey(); 26 | bool switchValue = false; 27 | late Query postQuery; 28 | late BannerAd _bannerAd; 29 | bool _isBannerAdReady = false; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | 35 | _database.ref().child(nodeName).onChildAdded.listen(_childAdded); 36 | _database.ref().child(nodeName).onChildRemoved.listen(_childRemoves); 37 | _database.ref().child(nodeName).onChildChanged.listen(_childChanged); 38 | postQuery = _database.ref().child('posts'); 39 | 40 | _bannerAd = BannerAd( 41 | adUnitId: AdHelper.bannerAdUnitId, 42 | request: const AdRequest(), 43 | size: AdSize.banner, 44 | listener: BannerAdListener( 45 | onAdLoaded: (_) { 46 | setState(() { 47 | _isBannerAdReady = true; 48 | }); 49 | }, 50 | onAdFailedToLoad: (Ad ad, LoadAdError err) { 51 | debugPrint('Failed to load a banner ad: ${err.message}'); 52 | _isBannerAdReady = false; 53 | ad.dispose(); 54 | }, 55 | ), 56 | ); 57 | 58 | _bannerAd.load(); 59 | } 60 | 61 | @override 62 | void dispose() { 63 | _bannerAd.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | final DarkThemeProvider themeChange = 70 | Provider.of(context); 71 | return Scaffold( 72 | key: _globalKey, 73 | appBar: AppBar( 74 | actions: [ 75 | IconButton( 76 | icon: const Icon(Icons.receipt), 77 | onPressed: () { 78 | Navigator.pushNamed(context, RouteConstant.MEDIUM_ARTICLES); 79 | }, 80 | ) 81 | ], 82 | title: Image.asset( 83 | themeChange.darkTheme 84 | ? 'assets/blog_flutter_dark.png' 85 | : 'assets/blog_flutter_light.png', 86 | height: kToolbarHeight + 100, 87 | ), 88 | leading: IconButton( 89 | icon: const Icon(Icons.menu_rounded), 90 | onPressed: () => _globalKey.currentState!.openDrawer()), 91 | ), 92 | body: Stack( 93 | children: [ 94 | Column( 95 | children: [ 96 | Visibility( 97 | visible: postsList.isEmpty, 98 | child: Center( 99 | child: Container( 100 | alignment: Alignment.center, 101 | child: const Text('No post to show'), 102 | ), 103 | ), 104 | ), 105 | Visibility( 106 | visible: postsList.isNotEmpty, 107 | child: Flexible( 108 | child: FirebaseAnimatedList( 109 | query: postQuery, 110 | itemBuilder: (_, DataSnapshot snap, 111 | Animation animation, int index) { 112 | if (snap.exists) 113 | return PostCard(post: postsList[index]); 114 | return const Center( 115 | child: CircularProgressIndicator()); 116 | }), 117 | ), 118 | ), 119 | ], 120 | ), 121 | if (_isBannerAdReady) 122 | Align( 123 | alignment: Alignment.bottomCenter, 124 | child: SizedBox( 125 | width: _bannerAd.size.width.toDouble(), 126 | height: _bannerAd.size.height.toDouble(), 127 | child: AdWidget(ad: _bannerAd), 128 | ), 129 | ), 130 | ], 131 | ), 132 | floatingActionButton: Padding( 133 | padding: EdgeInsets.only(bottom: _bannerAd.size.height + 10), 134 | child: FloatingActionButton( 135 | onPressed: () { 136 | Navigator.pushNamed(context, RouteConstant.ADD_POST); 137 | }, 138 | tooltip: 'Add a post', 139 | child: const Icon( 140 | Icons.add, 141 | ), 142 | ), 143 | ), 144 | floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, 145 | drawer: BlogDrawer()); 146 | } 147 | 148 | void _childAdded(dynamic event) { 149 | setState(() { 150 | postsList.add(Post.fromSnapshot(event.snapshot)); 151 | }); 152 | } 153 | 154 | void _childRemoves(dynamic event) { 155 | final Post deletedPost = postsList.singleWhere((Post post) { 156 | return post.key == event.snapshot.key; 157 | }); 158 | 159 | setState(() { 160 | postsList.removeAt(postsList.indexOf(deletedPost)); 161 | }); 162 | } 163 | 164 | void _childChanged(dynamic event) { 165 | final Post changedPost = postsList.singleWhere((Post post) { 166 | return post.key == event.snapshot.key; 167 | }); 168 | setState(() { 169 | postsList[postsList.indexOf(changedPost)] = 170 | Post.fromSnapshot(event.snapshot); 171 | }); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /lib/views/medium/medium_articles.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/models/article.dart'; 2 | import 'package:blog_app/providers/medium_article_notifier.dart'; 3 | import 'package:blog_app/providers/theme_notifier.dart'; 4 | import 'package:blog_app/routes/route_constants.dart'; 5 | import 'package:blog_app/services/fetch_medium_articles_service.dart'; 6 | import 'package:cached_network_image/cached_network_image.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:provider/provider.dart'; 9 | 10 | class MediumArticles extends StatefulWidget { 11 | @override 12 | State createState() { 13 | return MediumArticlesState(); 14 | } 15 | } 16 | 17 | class MediumArticlesState extends State { 18 | late List selected; 19 | final GlobalKey _formKey = GlobalKey(); 20 | final TextEditingController myController = TextEditingController(); 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void dispose() { 29 | // Clean up the controller when the widget is disposed. 30 | myController.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final DarkThemeProvider themeChange = 37 | Provider.of(context); 38 | final MediumArticleNotifier mediumArticleNotifier = 39 | Provider.of(context); 40 | final List articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------
articleList = mediumArticleNotifier.getArticleList(); 41 | return Scaffold( 42 | appBar: AppBar( 43 | leading: IconButton( 44 | icon: const Icon(Icons.arrow_back_ios_rounded), 45 | onPressed: () => Navigator.of(context).pop(), 46 | ), 47 | title: const Text('Search Medium Articles')), 48 | body: Padding( 49 | padding: const EdgeInsets.symmetric(horizontal: 8), 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 52 | children: [ 53 | Expanded( 54 | child: articleList.isNotEmpty 55 | ? ListView.builder( 56 | shrinkWrap: true, 57 | itemCount: articleList.length, 58 | itemBuilder: (_, int index) { 59 | final Article article = articleList[index]; 60 | return GestureDetector( 61 | onTap: () { 62 | Navigator.pushNamed(context, 63 | RouteConstant.MEDIUM_ARTICLES_WEB_VIEW, 64 | arguments: { 65 | 'title': article.title, 66 | 'url': article.link 67 | }); 68 | }, 69 | child: Card( 70 | shape: RoundedRectangleBorder( 71 | borderRadius: BorderRadius.circular(10)), 72 | child: Column( 73 | crossAxisAlignment: CrossAxisAlignment.start, 74 | children: [ 75 | ClipRRect( 76 | borderRadius: const BorderRadius.only( 77 | topLeft: Radius.circular(10), 78 | topRight: Radius.circular(10)), 79 | child: CachedNetworkImage( 80 | imageUrl: article.thumbnail, 81 | ), 82 | ), 83 | Padding( 84 | padding: const EdgeInsets.all(8.0), 85 | child: Text( 86 | article.title, 87 | textAlign: TextAlign.center, 88 | style: const TextStyle( 89 | fontWeight: FontWeight.bold, 90 | fontSize: 16), 91 | ), 92 | ), 93 | Padding( 94 | padding: const EdgeInsets.all(8.0), 95 | child: Text( 96 | 'Author: ${article.author}', 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ); 103 | }, 104 | ) 105 | : Center( 106 | child: mediumArticleNotifier.getLoader() 107 | ? const CircularProgressIndicator() 108 | : const Text( 109 | 'Search Medium Articles by Author name.'), 110 | ), 111 | ), 112 | Padding( 113 | padding: const EdgeInsets.symmetric(vertical: 5), 114 | child: Form( 115 | key: _formKey, 116 | child: Row( 117 | crossAxisAlignment: CrossAxisAlignment.start, 118 | children: [ 119 | Expanded( 120 | child: TextFormField( 121 | controller: myController, 122 | keyboardType: TextInputType.text, 123 | decoration: const InputDecoration( 124 | border: OutlineInputBorder(), 125 | contentPadding: EdgeInsets.only( 126 | left: 15, bottom: 11, top: 11, right: 15), 127 | hintText: 'Enter Username', 128 | // hintStyle: TextStyle(color: Colors.white), 129 | ), 130 | validator: (String? val) { 131 | if (val!.isEmpty) { 132 | return "Username can't be empty"; 133 | } 134 | }, 135 | style: TextStyle( 136 | color: themeChange.darkTheme 137 | ? Colors.white 138 | : Colors.black), 139 | ), 140 | ), 141 | const SizedBox( 142 | width: 10, 143 | ), 144 | ElevatedButton( 145 | onPressed: () { 146 | if (mediumArticleNotifier 147 | .getArticleList() 148 | .isNotEmpty) { 149 | mediumArticleNotifier.clearArticleList(); 150 | mediumArticleNotifier.setloader(false); 151 | } else { 152 | if (_formKey.currentState!.validate()) { 153 | _formKey.currentState!.save(); 154 | mediumArticleNotifier.setloader(true); 155 | FetchMediumArticleService.getPosts( 156 | mediumArticleNotifier, myController.text); 157 | } 158 | } 159 | }, 160 | child: Text(articleList.isEmpty ? 'Fetch' : 'Clear'), 161 | ), 162 | ], 163 | ), 164 | ), 165 | ) 166 | ], 167 | )), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Blog App 2 | 3 | Table of contents 4 | ================= 5 | 6 | 7 | * [About this app](#about-this-app) 8 | * [App Screens](#app-screens) 9 | * [Getting Started](#getting-started) 10 | * [Setting up the Project](#setting-up-the-project) 11 | * [Issues](#issues) 12 | * [Contributing](#contributing) 13 | * [Code of Conduct](#code-of-conduct) 14 | * [License](#license) 15 | 16 | 17 | ## About this app 18 | An anonymous blog creation application. It is provides real-time blog creation without any communication and account 19 | creation. It is Created using Flutter SDK and utilizing Firebase as backend. 20 | 21 | 22 | 23 | [](https://www.buymeacoffee.com/himanshusharma) 24 | 25 | ## App Screens 26 | Images of the app while using Dark Mode - 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Images of the app while using Light Mode - 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## Getting Started 45 | 46 | This project is a starting point for a Flutter application. 47 | 48 | A few resources to get you started if this is your first Flutter project: 49 | 50 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 51 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 52 | 53 | For help getting started with Flutter, view our 54 | [online documentation](https://flutter.dev/docs), which offers tutorials, 55 | samples, guidance on mobile development, and a full API reference. 56 | 57 | ## Setting up the Project 58 | 59 | 1. Go to [the project repo](https://github.com/himanshusharma89/Flutter-Blog-App) and fork it by clicking "Fork" 60 | 2. If you are working on Windows, download [Git Bash for Windows](https://git-for-windows.github.io/) to get a full Unix bash with Git functionality 61 | 3. Clone the repo to your desktop `git clone https://github.com/YOUR_USERNAME/Flutter-Blog-App.git` 62 | 4. Open the project 63 | 64 | ## Issues 65 | Please file specific issues, bugs, or feature requests in our [issue tracker](https://github.com/himanshusharma89/Flutter-Blog-App/issues). Follow the 66 | issue template provided while creating a new issue. 67 | 68 | ## Contributing 69 | If you wish to contribute a change to any of the existing features in this repo, please review our [contribution guide](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CONTRIBUTING.md) and send a [pull request](https://github.com/himanshusharma89/Flutter-Blog-App/pulls). 70 | 71 | ## Code of Conduct 72 | We follow certain guidelines in order to maintain this repository.Please find our [code of conduct](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/CODE_OF_CONDUCT.md) and read it carefully. 73 | 74 | ## License 75 | Distributed under the CC0-1.0 License.See [LICENSE](https://github.com/himanshusharma89/Flutter-Blog-App/blob/master/LICENSE) for more information. 76 | 77 | ## Developer ✨ 78 | 79 | 80 | 81 | Himanshu Sharma 82 | 83 | 84 | 85 | 86 | 87 | ## Contributors ✨ 88 | 89 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 90 | 91 | 92 | 93 | 94 | 95 | 96 | Shubham Chhimpa💻 97 | Carlos Felix🎨 98 | Dimas Rangga💻 99 | Arbaz Mustufa Diwan💻 100 | Adrien💻 101 | Promise Amadi🎨 102 | Daru Anugerah Setiawan🎨 103 | 104 | 105 | Yash Ajgaonkar📖 106 | Dhruv Sachdev💻 107 | Janhavi💻 🎨 108 | Saransh Chopra🎨 📖 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 118 | 119 | 120 | **Made with ♥ by Himanshu Sharma** 121 | -------------------------------------------------------------------------------- /lib/views/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:blog_app/helpers/constants.dart'; 2 | import 'package:blog_app/main.dart'; 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class About extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text( 12 | 'About', 13 | ), 14 | leading: IconButton( 15 | icon: const Icon( 16 | Icons.arrow_back_ios, 17 | ), 18 | onPressed: () { 19 | Navigator.pop(context); 20 | }, 21 | ), 22 | ), 23 | body: SingleChildScrollView( 24 | child: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), 26 | child: Column( 27 | children: [ 28 | const Text( 29 | 'Want to pen down your thoughts in the form of a blog anonymously?', 30 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 31 | ), 32 | const Text( 33 | 'This is just the App for you! You can post your blogs and no one can know about the original poster.', 34 | style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w700), 35 | ), 36 | const SizedBox( 37 | height: 20, 38 | ), 39 | Stack( 40 | children: [ 41 | SizedBox( 42 | width: double.infinity, 43 | child: Card( 44 | elevation: 5.0, 45 | margin: const EdgeInsets.only(top: 45.0), 46 | child: Padding( 47 | padding: const EdgeInsets.only(top: 40.0, bottom: 15), 48 | child: Column( 49 | children: [ 50 | const Text('Himanshu Sharma', 51 | style: TextStyle( 52 | fontSize: 16.0, 53 | fontWeight: FontWeight.w600, 54 | )), 55 | const SizedBox( 56 | height: 10.0, 57 | ), 58 | Row( 59 | mainAxisAlignment: MainAxisAlignment.center, 60 | children: social 61 | .map( 62 | (Map e) => Padding( 63 | padding: const EdgeInsets.symmetric( 64 | horizontal: 8), 65 | child: GestureDetector( 66 | onTap: () => 67 | launcher.launcher(e['URL']!), 68 | child: CachedNetworkImage( 69 | imageUrl: e['iconURL']!, 70 | height: 26, 71 | width: 26, 72 | placeholder: (_, String str) => 73 | const CircularProgressIndicator(), 74 | ), 75 | ), 76 | ), 77 | ) 78 | .toList()), 79 | const SizedBox( 80 | height: 10.0, 81 | ), 82 | const Text( 83 | 'The Developer behind this project', 84 | style: TextStyle(fontSize: 15), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ), 90 | ), 91 | const Positioned( 92 | top: .0, 93 | left: .0, 94 | right: .0, 95 | child: Center( 96 | child: CircleAvatar( 97 | radius: 40.0, 98 | backgroundImage: CachedNetworkImageProvider( 99 | 'https://avatars0.githubusercontent.com/u/44980497?v=4'), 100 | ), 101 | ), 102 | ) 103 | ], 104 | ), 105 | Card( 106 | margin: const EdgeInsets.only(top: 20), 107 | elevation: 5, 108 | child: Padding( 109 | padding: const EdgeInsets.symmetric(vertical: 10), 110 | child: Column( 111 | mainAxisSize: MainAxisSize.min, 112 | children: [ 113 | const Text('Contributors ✨', 114 | style: TextStyle( 115 | fontSize: 22.0, fontWeight: FontWeight.w600)), 116 | const SizedBox(height: 10.0), 117 | GridView.builder( 118 | itemCount: contributors.length, 119 | gridDelegate: 120 | const SliverGridDelegateWithFixedCrossAxisCount( 121 | crossAxisCount: 3, 122 | mainAxisSpacing: 4, 123 | crossAxisSpacing: 4, 124 | childAspectRatio: 1 / 0.9), 125 | primary: false, 126 | shrinkWrap: true, 127 | physics: const NeverScrollableScrollPhysics(), 128 | itemBuilder: (_, int index) { 129 | return Column( 130 | mainAxisAlignment: MainAxisAlignment.center, 131 | children: [ 132 | Container( 133 | height: 60, 134 | width: 60, 135 | decoration: BoxDecoration( 136 | shape: BoxShape.circle, 137 | image: DecorationImage( 138 | image: CachedNetworkImageProvider( 139 | contributors[index]['avatar_url'] 140 | as String))), 141 | ), 142 | const SizedBox( 143 | height: 5, 144 | ), 145 | Text( 146 | contributors[index]['login'] as String, 147 | style: const TextStyle(fontSize: 13), 148 | ) 149 | ], 150 | ); 151 | }), 152 | ], 153 | ), 154 | ), 155 | ), 156 | SizedBox( 157 | width: double.infinity, 158 | child: Card( 159 | elevation: 5.0, 160 | margin: const EdgeInsets.only(top: 20.0), 161 | child: Padding( 162 | padding: const EdgeInsets.symmetric( 163 | vertical: 15, horizontal: 10), 164 | child: Column( 165 | children: [ 166 | const Text('Contributing', 167 | style: TextStyle( 168 | fontSize: 22.0, fontWeight: FontWeight.w600)), 169 | const SizedBox(height: 10.0), 170 | const Text( 171 | 'If you wish to contribute a change to any of the existing features in this application, please review our contribution guide and send a pull request.', 172 | textAlign: TextAlign.center, 173 | ), 174 | const SizedBox(height: 10.0), 175 | GestureDetector( 176 | onTap: () => launcher.launcher( 177 | 'https://github.com/himanshusharma89/Flutter-Blog-App'), 178 | child: Image.asset( 179 | 'assets/contribute_icon.png', 180 | height: 26, 181 | width: 26, 182 | ), 183 | ), 184 | ], 185 | ), 186 | ), 187 | ), 188 | ), 189 | const SizedBox( 190 | height: 20, 191 | ) 192 | ], 193 | ), 194 | ), 195 | ), 196 | ); 197 | } 198 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 17 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD53AD0D5155C97E9614861A /* Pods_Runner.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 40 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 41 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 43 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 44 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 45 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 47 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | D590725C5691A6D464D5308E /* Pods_Runner.framework in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 5B8A2328140D4D916A901C11 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | BD53AD0D5155C97E9614861A /* Pods_Runner.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 9740EEB11CF90186004384FC /* Flutter */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 81 | ); 82 | name = Flutter; 83 | sourceTree = ""; 84 | }; 85 | 97C146E51CF9000F007C117D = { 86 | isa = PBXGroup; 87 | children = ( 88 | 9740EEB11CF90186004384FC /* Flutter */, 89 | 97C146F01CF9000F007C117D /* Runner */, 90 | 97C146EF1CF9000F007C117D /* Products */, 91 | EF092AF938DD7D7F80D604CE /* Pods */, 92 | 5B8A2328140D4D916A901C11 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 97C146EF1CF9000F007C117D /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 97C146EE1CF9000F007C117D /* Runner.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 97C146F01CF9000F007C117D /* Runner */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 35EAC6AE252F3BE0005AB3F0 /* GoogleService-Info.plist */, 112 | 97C146F11CF9000F007C117D /* Supporting Files */, 113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 115 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 116 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | ); 125 | name = "Supporting Files"; 126 | sourceTree = ""; 127 | }; 128 | EF092AF938DD7D7F80D604CE /* Pods */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | 1370F633D768AB08476F98B1 /* Pods-Runner.debug.xcconfig */, 132 | 594872D5FD0760EDA4A84435 /* Pods-Runner.release.xcconfig */, 133 | E66F0EB2BA3F8768A20A18B0 /* Pods-Runner.profile.xcconfig */, 134 | ); 135 | path = Pods; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | 97C146ED1CF9000F007C117D /* Runner */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 144 | buildPhases = ( 145 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */, 146 | 9740EEB61CF901F6004384FC /* Run Script */, 147 | 97C146EA1CF9000F007C117D /* Sources */, 148 | 97C146EB1CF9000F007C117D /* Frameworks */, 149 | 97C146EC1CF9000F007C117D /* Resources */, 150 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 151 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 152 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1020; 170 | ORGANIZATIONNAME = "The Chromium Authors"; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | LastSwiftMigration = 1100; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = en; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 97C146E51CF9000F007C117D; 187 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 97C146ED1CF9000F007C117D /* Runner */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 97C146EC1CF9000F007C117D /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 202 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 203 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 204 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 205 | 35EAC6AF252F3BE0005AB3F0 /* GoogleService-Info.plist in Resources */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXShellScriptBuildPhase section */ 212 | 30952CC81AE78D45210277FF /* [CP] Embed Pods Frameworks */ = { 213 | isa = PBXShellScriptBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | ); 217 | inputPaths = ( 218 | ); 219 | name = "[CP] Embed Pods Frameworks"; 220 | outputPaths = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | shellPath = /bin/sh; 224 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 225 | showEnvVarsInLog = 0; 226 | }; 227 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 228 | isa = PBXShellScriptBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | ); 232 | inputPaths = ( 233 | ); 234 | name = "Thin Binary"; 235 | outputPaths = ( 236 | ); 237 | runOnlyForDeploymentPostprocessing = 0; 238 | shellPath = /bin/sh; 239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 240 | }; 241 | 673139FEB6DF842BDDD11942 /* [CP] Check Pods Manifest.lock */ = { 242 | isa = PBXShellScriptBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | ); 246 | inputFileListPaths = ( 247 | ); 248 | inputPaths = ( 249 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 250 | "${PODS_ROOT}/Manifest.lock", 251 | ); 252 | name = "[CP] Check Pods Manifest.lock"; 253 | outputFileListPaths = ( 254 | ); 255 | outputPaths = ( 256 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | shellPath = /bin/sh; 260 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 261 | showEnvVarsInLog = 0; 262 | }; 263 | 9740EEB61CF901F6004384FC /* Run Script */ = { 264 | isa = PBXShellScriptBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | ); 268 | inputPaths = ( 269 | ); 270 | name = "Run Script"; 271 | outputPaths = ( 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 276 | }; 277 | /* End PBXShellScriptBuildPhase section */ 278 | 279 | /* Begin PBXSourcesBuildPhase section */ 280 | 97C146EA1CF9000F007C117D /* Sources */ = { 281 | isa = PBXSourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 285 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 286 | ); 287 | runOnlyForDeploymentPostprocessing = 0; 288 | }; 289 | /* End PBXSourcesBuildPhase section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 97C146FB1CF9000F007C117D /* Base */, 296 | ); 297 | name = Main.storyboard; 298 | sourceTree = ""; 299 | }; 300 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 97C147001CF9000F007C117D /* Base */, 304 | ); 305 | name = LaunchScreen.storyboard; 306 | sourceTree = ""; 307 | }; 308 | /* End PBXVariantGroup section */ 309 | 310 | /* Begin XCBuildConfiguration section */ 311 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | ALWAYS_SEARCH_USER_PATHS = NO; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 317 | CLANG_CXX_LIBRARY = "libc++"; 318 | CLANG_ENABLE_MODULES = YES; 319 | CLANG_ENABLE_OBJC_ARC = YES; 320 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 321 | CLANG_WARN_BOOL_CONVERSION = YES; 322 | CLANG_WARN_COMMA = YES; 323 | CLANG_WARN_CONSTANT_CONVERSION = YES; 324 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INFINITE_RECURSION = YES; 329 | CLANG_WARN_INT_CONVERSION = YES; 330 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 332 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 333 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 334 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 335 | CLANG_WARN_STRICT_PROTOTYPES = YES; 336 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = NO; 341 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 342 | ENABLE_NS_ASSERTIONS = NO; 343 | ENABLE_STRICT_OBJC_MSGSEND = YES; 344 | GCC_C_LANGUAGE_STANDARD = gnu99; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = iphoneos; 355 | SUPPORTED_PLATFORMS = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | VALIDATE_PRODUCT = YES; 358 | }; 359 | name = Profile; 360 | }; 361 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 362 | isa = XCBuildConfiguration; 363 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 364 | buildSettings = { 365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 366 | CLANG_ENABLE_MODULES = YES; 367 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 368 | ENABLE_BITCODE = NO; 369 | FRAMEWORK_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "$(PROJECT_DIR)/Flutter", 372 | ); 373 | INFOPLIST_FILE = Runner/Info.plist; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 375 | LIBRARY_SEARCH_PATHS = ( 376 | "$(inherited)", 377 | "$(PROJECT_DIR)/Flutter", 378 | ); 379 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 382 | SWIFT_VERSION = 5.0; 383 | VERSIONING_SYSTEM = "apple-generic"; 384 | }; 385 | name = Profile; 386 | }; 387 | 97C147031CF9000F007C117D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 401 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 402 | CLANG_WARN_EMPTY_BODY = YES; 403 | CLANG_WARN_ENUM_CONVERSION = YES; 404 | CLANG_WARN_INFINITE_RECURSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 408 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 410 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 411 | CLANG_WARN_STRICT_PROTOTYPES = YES; 412 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 413 | CLANG_WARN_UNREACHABLE_CODE = YES; 414 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | COPY_PHASE_STRIP = NO; 417 | DEBUG_INFORMATION_FORMAT = dwarf; 418 | ENABLE_STRICT_OBJC_MSGSEND = YES; 419 | ENABLE_TESTABILITY = YES; 420 | GCC_C_LANGUAGE_STANDARD = gnu99; 421 | GCC_DYNAMIC_NO_PIC = NO; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_OPTIMIZATION_LEVEL = 0; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 429 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 430 | GCC_WARN_UNDECLARED_SELECTOR = YES; 431 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 432 | GCC_WARN_UNUSED_FUNCTION = YES; 433 | GCC_WARN_UNUSED_VARIABLE = YES; 434 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 435 | MTL_ENABLE_DEBUG_INFO = YES; 436 | ONLY_ACTIVE_ARCH = YES; 437 | SDKROOT = iphoneos; 438 | TARGETED_DEVICE_FAMILY = "1,2"; 439 | }; 440 | name = Debug; 441 | }; 442 | 97C147041CF9000F007C117D /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ALWAYS_SEARCH_USER_PATHS = NO; 446 | CLANG_ANALYZER_NONNULL = YES; 447 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 448 | CLANG_CXX_LIBRARY = "libc++"; 449 | CLANG_ENABLE_MODULES = YES; 450 | CLANG_ENABLE_OBJC_ARC = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INFINITE_RECURSION = YES; 460 | CLANG_WARN_INT_CONVERSION = YES; 461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 466 | CLANG_WARN_STRICT_PROTOTYPES = YES; 467 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 468 | CLANG_WARN_UNREACHABLE_CODE = YES; 469 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 470 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 471 | COPY_PHASE_STRIP = NO; 472 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 473 | ENABLE_NS_ASSERTIONS = NO; 474 | ENABLE_STRICT_OBJC_MSGSEND = YES; 475 | GCC_C_LANGUAGE_STANDARD = gnu99; 476 | GCC_NO_COMMON_BLOCKS = YES; 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 484 | MTL_ENABLE_DEBUG_INFO = NO; 485 | SDKROOT = iphoneos; 486 | SUPPORTED_PLATFORMS = iphoneos; 487 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 488 | TARGETED_DEVICE_FAMILY = "1,2"; 489 | VALIDATE_PRODUCT = YES; 490 | }; 491 | name = Release; 492 | }; 493 | 97C147061CF9000F007C117D /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 496 | buildSettings = { 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | CLANG_ENABLE_MODULES = YES; 499 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 500 | ENABLE_BITCODE = NO; 501 | FRAMEWORK_SEARCH_PATHS = ( 502 | "$(inherited)", 503 | "$(PROJECT_DIR)/Flutter", 504 | ); 505 | INFOPLIST_FILE = Runner/Info.plist; 506 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 507 | LIBRARY_SEARCH_PATHS = ( 508 | "$(inherited)", 509 | "$(PROJECT_DIR)/Flutter", 510 | ); 511 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 512 | PRODUCT_NAME = "$(TARGET_NAME)"; 513 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 514 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 515 | SWIFT_VERSION = 5.0; 516 | VERSIONING_SYSTEM = "apple-generic"; 517 | }; 518 | name = Debug; 519 | }; 520 | 97C147071CF9000F007C117D /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 523 | buildSettings = { 524 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 525 | CLANG_ENABLE_MODULES = YES; 526 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 527 | ENABLE_BITCODE = NO; 528 | FRAMEWORK_SEARCH_PATHS = ( 529 | "$(inherited)", 530 | "$(PROJECT_DIR)/Flutter", 531 | ); 532 | INFOPLIST_FILE = Runner/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | LIBRARY_SEARCH_PATHS = ( 535 | "$(inherited)", 536 | "$(PROJECT_DIR)/Flutter", 537 | ); 538 | PRODUCT_BUNDLE_IDENTIFIER = com.project.blogApp; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 541 | SWIFT_VERSION = 5.0; 542 | VERSIONING_SYSTEM = "apple-generic"; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 97C147031CF9000F007C117D /* Debug */, 553 | 97C147041CF9000F007C117D /* Release */, 554 | 249021D3217E4FDB00AE95B9 /* Profile */, 555 | ); 556 | defaultConfigurationIsVisible = 0; 557 | defaultConfigurationName = Release; 558 | }; 559 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 97C147061CF9000F007C117D /* Debug */, 563 | 97C147071CF9000F007C117D /* Release */, 564 | 249021D4217E4FDB00AE95B9 /* Profile */, 565 | ); 566 | defaultConfigurationIsVisible = 0; 567 | defaultConfigurationName = Release; 568 | }; 569 | /* End XCConfigurationList section */ 570 | }; 571 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 572 | } 573 | --------------------------------------------------------------------------------