From a35b6e5ef410942b5987bad6f583d300c1ef9628 Mon Sep 17 00:00:00 2001 From: Hugh Ratsch Date: Sat, 5 Oct 2024 16:05:26 -0500 Subject: [PATCH] added resources, coding challenges, and more study guides --- ...arning Challenge Set Beginner to Senior.md | 82 ++++++ index.md | 5 +- ...omprehensive Flutter Learning Resources.md | 78 +++++ .../Asynchronous Programming in Dart.md | 83 ++++++ study_guide/Dart Collections.md | 160 +++++++++++ study_guide/Error Handling in Dart.md | 152 ++++++++++ study_guide/Extension Methods in Dart.md | 100 +++++++ study_guide/Flutter Animations.md | 206 ++++++++++++++ study_guide/Flutter Navigation and Routing.md | 267 ++++++++++++++++++ study_guide/Flutter Widget Lifecycle.md | 159 +++++++++++ ...Functional Programming Concepts in Dart.md | 141 +++++++++ study_guide/Generics in Dart.md | 104 +++++++ study_guide/Mixins in Dart.md | 98 +++++++ study_guide/Null Safety in Dart.md | 82 ++++++ study_guide/State Management in Flutter.md | 159 +++++++++++ ..._dart_and_flutter_a_comprehensive_guide.md | 0 ...ncepts_dart_flutter_comprehensive_guide.md | 0 17 files changed, 1875 insertions(+), 1 deletion(-) create mode 100644 coding_challenges/Flutter Learning Challenge Set Beginner to Senior.md create mode 100644 resources/Comprehensive Flutter Learning Resources.md create mode 100644 study_guide/Asynchronous Programming in Dart.md create mode 100644 study_guide/Dart Collections.md create mode 100644 study_guide/Error Handling in Dart.md create mode 100644 study_guide/Extension Methods in Dart.md create mode 100644 study_guide/Flutter Animations.md create mode 100644 study_guide/Flutter Navigation and Routing.md create mode 100644 study_guide/Flutter Widget Lifecycle.md create mode 100644 study_guide/Functional Programming Concepts in Dart.md create mode 100644 study_guide/Generics in Dart.md create mode 100644 study_guide/Mixins in Dart.md create mode 100644 study_guide/Null Safety in Dart.md create mode 100644 study_guide/State Management in Flutter.md rename {posts => study_guide}/class_constructors_in_dart_and_flutter_a_comprehensive_guide.md (100%) rename {posts => study_guide}/oop_concepts_dart_flutter_comprehensive_guide.md (100%) diff --git a/coding_challenges/Flutter Learning Challenge Set Beginner to Senior.md b/coding_challenges/Flutter Learning Challenge Set Beginner to Senior.md new file mode 100644 index 0000000..b6c6410 --- /dev/null +++ b/coding_challenges/Flutter Learning Challenge Set Beginner to Senior.md @@ -0,0 +1,82 @@ +# Flutter Learning Challenge Set: Beginner to Senior + +## Beginner Challenge: Task List App + +Create a simple task list app that allows users to add, view, and delete tasks. + +### Requirements: +1. Use a `StatefulWidget` for the main screen. +2. Implement a list to store tasks (use the `List` collection). +3. Create a text input field for adding new tasks. +4. Display tasks in a `ListView`. +5. Add a delete functionality for each task. +6. Use basic state management within the `StatefulWidget`. + +### Concepts covered: +- Basic Flutter widgets +- StatefulWidget lifecycle +- Dart collections (List) +- Simple state management + +## Junior Challenge: Weather Forecast App + +Extend the task list app into a weather forecast app that fetches data from an API and uses more advanced Flutter concepts. + +### Requirements: +1. Create a weather forecast screen that displays current weather and a 5-day forecast. +2. Implement navigation to switch between the task list and weather forecast screens. +3. Use `http` package to fetch weather data from a free API (e.g., OpenWeatherMap). +4. Implement error handling for API calls. +5. Use `Provider` for state management. +6. Create custom widgets for weather information display. +7. Add animations for loading states and transitions. + +### Concepts covered: +- Flutter navigation +- Asynchronous programming (Futures) +- Error handling +- HTTP requests +- Provider for state management +- Custom widgets +- Basic animations + +## Senior Challenge: Smart Home Control App + +Develop a comprehensive smart home control app that incorporates advanced Flutter concepts and integrates features from the previous challenges. + +### Requirements: +1. Create a dashboard that displays: + - Task list widget (from beginner challenge) + - Weather widget (from junior challenge) + - Smart device control widgets (new feature) +2. Implement user authentication (you can mock this locally). +3. Use Firebase (or a mock service) for backend data storage and real-time updates. +4. Implement advanced navigation using Navigator 2.0. +5. Create custom animations for device control interactions. +6. Use `BLoC` pattern for state management. +7. Implement unit and widget tests for critical components. +8. Add theme switching capability (light/dark mode). +9. Optimize the app for different screen sizes (responsive design). +10. Implement local notifications for task reminders and device alerts. + +### Concepts covered: +- Advanced state management (BLoC) +- Navigator 2.0 +- Firebase integration (or mocking advanced backend services) +- Custom animations +- Unit and widget testing +- Theme management +- Responsive design +- Local notifications + +## Building on Previous Challenges: +- The task list from the beginner challenge becomes a widget in the senior challenge's dashboard. +- The weather forecast from the junior challenge is also integrated into the senior challenge's dashboard. +- The navigation and state management evolve from basic concepts in the beginner challenge to more advanced implementations in the senior challenge. + +## Future Assistance Prompt: +If you need help with these challenges in the future, use the following prompt: + +"I'm working on the Flutter Learning Challenge Set that was generated in a previous conversation. I'm currently on the [Beginner/Junior/Senior] challenge, specifically struggling with [describe the specific part you're stuck on]. Can you provide guidance on how to approach this problem, keeping in mind the concepts that should be demonstrated at this level of the challenge?" + +This prompt will help the AI assistant understand the context of your question and provide appropriate guidance based on the challenge level and specific concepts you should be working with. \ No newline at end of file diff --git a/index.md b/index.md index 29627a2..50bfc13 100644 --- a/index.md +++ b/index.md @@ -6,4 +6,7 @@ tags: [] This is the start of my digital garden. I have high hopes that it will grow into something magical! -\- [Study Notes](/posts) +\- [Study Notes From Code Alongs](/posts) +\- [Study Guide](/study_guide) +\- [Resources](/resources) +\- [Coding Challenges](/coding_challenges) diff --git a/resources/Comprehensive Flutter Learning Resources.md b/resources/Comprehensive Flutter Learning Resources.md new file mode 100644 index 0000000..465c95a --- /dev/null +++ b/resources/Comprehensive Flutter Learning Resources.md @@ -0,0 +1,78 @@ +# 🚀 Ultimate Flutter Learning Resources Guide + +## 📚 Official Resources + +### 1. Flutter's Official Documentation +The holy grail of Flutter knowledge, offering in-depth explanations of widgets, APIs, and core concepts. + +🔗 [Dive into the Docs](https://docs.flutter.dev/) + +### 2. Flutter's GitHub Repository +Explore the framework's source code, learn its inner workings, and find practical examples. + +🐙 [Uncover the Code](https://github.com/flutter/flutter) + +## 💬 Community Engagement + +### 3. Flutter's Community Forums +Connect with fellow developers, share experiences, and get your questions answered. + +🌐 [Join the Conversation](https://dev.to/t/flutter) + +### 4. Stack Overflow +A treasure trove of Flutter-related questions and expert answers. + +🔍 [Search for Solutions](https://stackoverflow.com/) + +### 5. Flutter's Official Discord Server +Real-time chat, quick problem-solving, and a vibrant developer community. + +🎮 [Enter the Discord](https://discord.com/invite/rflutterdev) + +### 6. Flutter's Official Slack Channel +Another hub for developer interactions and knowledge sharing. + +💼 [Slack Your Way In](https://fluttercommunity.dev/joinslack) + +## 📺 Video Tutorials + +### 7. YouTube Channels +Visual learners rejoice! These channels offer top-notch Flutter tutorials: + +- 🎥 The Flutter Channel +- 🎥 Flutter Explained +- 🎥 Flutter Development +- 🎥 The Flutter Tutorials + +## 📱 Stay Updated + +### 8. Flutter's Official Twitter Account +Get the latest Flutter news, tips, and community highlights in your feed. + +🐦 [Follow @flutterdev](https://twitter.com/flutterdev) + +## 🤝 In-Person Learning + +### 9. Flutter's Official Meetup Groups +Nothing beats face-to-face interactions. Find Flutter enthusiasts near you! + +🗺️ [Discover Local Meetups](https://www.meetup.com/pro/flutter) + +## 📖 Flutter Books + +### 10. Must-Read Flutter Books +Dive deep into Flutter with these comprehensive guides: + +- 📕 "Flutter in Action" +- 📗 "The Complete Flutter App Development Bootcamp" +- 📘 "Flutter: The Definitive Guide" + +## 💡 Pro Tips for Learning Flutter + +1. **Start with the basics**: Ensure you have a solid understanding of Dart before diving deep into Flutter. +2. **Build, build, build**: The best way to learn is by creating projects. Start small and gradually increase complexity. +3. **Engage with the community**: Don't hesitate to ask questions and share your progress. +4. **Stay updated**: Flutter evolves quickly. Keep an eye on the latest updates and best practices. +5. **Contribute to open source**: Once comfortable, consider contributing to Flutter packages or the framework itself. + +Remember, learning Flutter is a journey. Enjoy the process, stay curious, and happy coding! 🎉 \ No newline at end of file diff --git a/study_guide/Asynchronous Programming in Dart.md b/study_guide/Asynchronous Programming in Dart.md new file mode 100644 index 0000000..b32f811 --- /dev/null +++ b/study_guide/Asynchronous Programming in Dart.md @@ -0,0 +1,83 @@ +# Asynchronous Programming in Dart: A Comprehensive Guide + +Asynchronous programming is crucial in Dart and Flutter for handling operations that might take some time to complete, such as network requests or file I/O. This guide covers the key concepts and tools for asynchronous programming in Dart. + +## 1. Futures + +A Future represents a computation that doesn't complete immediately. It's a way to represent asynchronous operations. + +### Example: + +```dart +Future fetchUserOrder() { + return Future.delayed(Duration(seconds: 2), () => "Large Latte"); +} + +void main() { + print('Fetching user order...'); + fetchUserOrder().then((order) => print('Your order is: $order')); + print('This runs before the order is fetched!'); +} +``` + +## 2. Async and Await + +The `async` and `await` keywords provide a cleaner syntax for working with Futures. + +### Example: + +```dart +Future fetchUserOrder() { + return Future.delayed(Duration(seconds: 2), () => "Large Latte"); +} + +void main() async { + print('Fetching user order...'); + String order = await fetchUserOrder(); + print('Your order is: $order'); +} +``` + +## 3. Streams + +Streams provide a way to receive a sequence of events over time. + +### Example: + +```dart +Stream countStream(int max) async* { + for (int i = 1; i <= max; i++) { + yield i; + await Future.delayed(Duration(seconds: 1)); + } +} + +void main() { + countStream(5).listen((data) => print('Count: $data')); +} +``` + +## 4. StreamController + +StreamController gives you a way to create and manage your own streams. + +### Example: + +```dart +import 'dart:async'; + +void main() { + final controller = StreamController(); + + controller.stream.listen((data) => print('Received: $data')); + + controller.add('Hello'); + controller.add('World'); + + controller.close(); +} +``` + +## Conclusion + +Mastering asynchronous programming is essential for building responsive and efficient Dart and Flutter applications. Practice using Futures, async/await, Streams, and StreamControllers to become proficient in handling asynchronous operations. \ No newline at end of file diff --git a/study_guide/Dart Collections.md b/study_guide/Dart Collections.md new file mode 100644 index 0000000..d57f909 --- /dev/null +++ b/study_guide/Dart Collections.md @@ -0,0 +1,160 @@ +# Dart Collections: A Comprehensive Guide + +Dart provides several built-in collection types to help you manage and manipulate groups of objects. This guide covers the main collection types and their operations. + +## 1. Lists + +Lists are ordered, indexable collections of objects. + +### Example: + +```dart +void main() { + // Creating lists + var numbers = [1, 2, 3, 4, 5]; + List fruits = ['apple', 'banana', 'orange']; + + // Accessing elements + print(numbers[0]); // 1 + print(fruits.last); // orange + + // Adding elements + numbers.add(6); + fruits.addAll(['grape', 'mango']); + + // Removing elements + numbers.remove(3); + fruits.removeAt(0); + + // Other operations + print(numbers.length); // 5 + print(fruits.contains('banana')); // true + + // Iterating + for (var fruit in fruits) { + print(fruit); + } + + // Functional operations + var doubledNumbers = numbers.map((n) => n * 2).toList(); + var evenNumbers = numbers.where((n) => n.isEven).toList(); +} +``` + +## 2. Sets + +Sets are unordered collections of unique items. + +### Example: + +```dart +void main() { + // Creating sets + var uniqueNumbers = {1, 2, 3, 4, 5}; + Set uniqueFruits = {'apple', 'banana', 'orange'}; + + // Adding elements + uniqueNumbers.add(6); + uniqueFruits.addAll(['grape', 'mango']); + + // Removing elements + uniqueNumbers.remove(3); + + // Set operations + var setA = {1, 2, 3, 4}; + var setB = {3, 4, 5, 6}; + print(setA.union(setB)); // {1, 2, 3, 4, 5, 6} + print(setA.intersection(setB)); // {3, 4} + print(setA.difference(setB)); // {1, 2} + + // Checking membership + print(uniqueFruits.contains('banana')); // true + + // Iterating + for (var number in uniqueNumbers) { + print(number); + } +} +``` + +## 3. Maps + +Maps are collections of key-value pairs. + +### Example: + +```dart +void main() { + // Creating maps + var ages = {'Alice': 30, 'Bob': 25, 'Charlie': 35}; + Map scores = {'Math': 90, 'Science': 85, 'History': 95}; + + // Accessing values + print(ages['Alice']); // 30 + print(scores['Math']); // 90 + + // Adding or updating entries + ages['David'] = 28; + scores.addAll({'Geography': 88, 'Literature': 92}); + + // Removing entries + ages.remove('Bob'); + + // Checking keys and values + print(ages.containsKey('Charlie')); // true + print(scores.containsValue(100)); // false + + // Iterating + ages.forEach((key, value) { + print('$key is $value years old'); + }); + + // Getting all keys or values + print(scores.keys); + print(scores.values); +} +``` + +## 4. Collection Methods and Operations + +Dart provides many useful methods for working with collections. + +### Example: + +```dart +void main() { + var numbers = [1, 2, 3, 4, 5]; + + // Functional methods + var doubled = numbers.map((n) => n * 2); + var evenNumbers = numbers.where((n) => n.isEven); + var sum = numbers.reduce((a, b) => a + b); + var product = numbers.fold(1, (a, b) => a * b); + + print(doubled); // (2, 4, 6, 8, 10) + print(evenNumbers); // (2, 4) + print(sum); // 15 + print(product); // 120 + + // Sorting + var fruits = ['banana', 'apple', 'orange']; + fruits.sort(); + print(fruits); // [apple, banana, orange] + + // Reversing + var reversedNumbers = numbers.reversed; + print(reversedNumbers); // (5, 4, 3, 2, 1) + + // Shuffling + numbers.shuffle(); + print(numbers); // Random order + + // Finding elements + var firstEven = numbers.firstWhere((n) => n.isEven, orElse: () => -1); + print(firstEven); +} +``` + +## Conclusion + +Dart's collection types provide powerful tools for managing groups of objects. Understanding how to effectively use Lists, Sets, and Maps, along with their associated methods and operations, is crucial for efficient Dart programming. \ No newline at end of file diff --git a/study_guide/Error Handling in Dart.md b/study_guide/Error Handling in Dart.md new file mode 100644 index 0000000..32ced6a --- /dev/null +++ b/study_guide/Error Handling in Dart.md @@ -0,0 +1,152 @@ +# Error Handling in Dart: A Comprehensive Guide + +Proper error handling is crucial for writing robust and reliable Dart applications. This guide covers the main concepts and techniques for handling errors in Dart. + +## 1. Try-Catch Blocks + +The try-catch block is the primary mechanism for handling exceptions in Dart. + +### Example: + +```dart +void main() { + try { + int result = 12 ~/ 0; // Integer division by zero + print(result); + } on IntegerDivisionByZeroException { + print('Cannot divide by zero'); + } catch (e) { + print('An error occurred: $e'); + } finally { + print('This always executes'); + } +} +``` + +## 2. Throwing Exceptions + +You can throw exceptions using the `throw` keyword. + +### Example: + +```dart +void validateAge(int age) { + if (age < 0) { + throw ArgumentError('Age cannot be negative'); + } +} + +void main() { + try { + validateAge(-5); + } catch (e) { + print('Error: $e'); + } +} +``` + +## 3. Custom Exceptions + +You can create custom exception classes by extending `Exception` or `Error`. + +### Example: + +```dart +class InsufficientFundsException implements Exception { + final double balance; + final double withdrawal; + + InsufficientFundsException(this.balance, this.withdrawal); + + @override + String toString() => 'Insufficient funds: $balance available, $withdrawal requested'; +} + +void withdraw(double balance, double amount) { + if (amount > balance) { + throw InsufficientFundsException(balance, amount); + } + // Proceed with withdrawal +} + +void main() { + try { + withdraw(100, 150); + } on InsufficientFundsException catch (e) { + print(e); + } +} +``` + +## 4. Async Error Handling + +When working with asynchronous code, you can use try-catch with async-await. + +### Example: + +```dart +Future fetchData() async { + // Simulating network request + await Future.delayed(Duration(seconds: 2)); + throw Exception('Failed to fetch data'); +} + +void main() async { + try { + String data = await fetchData(); + print(data); + } catch (e) { + print('Error occurred: $e'); + } +} +``` + +## 5. Error Propagation + +You can rethrow exceptions to propagate them up the call stack. + +### Example: + +```dart +void processData(String data) { + try { + // Process data + throw FormatException('Invalid data format'); + } catch (e) { + print('Error in processData: $e'); + rethrow; // Rethrow the caught exception + } +} + +void main() { + try { + processData('some data'); + } catch (e) { + print('Error in main: $e'); + } +} +``` + +## 6. Using `assert` + +The `assert` statement is used to check boolean conditions during development. + +### Example: + +```dart +void setVolume(int volume) { + assert(volume >= 0 && volume <= 100, 'Volume must be between 0 and 100'); + // Set volume +} + +void main() { + setVolume(50); // OK + setVolume(150); // AssertionError in debug mode +} +``` + +Note: Assertions are only enabled in debug mode and are ignored in production code. + +## Conclusion + +Effective error handling is essential for creating robust Dart applications. By using these techniques, you can gracefully handle exceptions, provide meaningful error messages, and improve the reliability of your code. \ No newline at end of file diff --git a/study_guide/Extension Methods in Dart.md b/study_guide/Extension Methods in Dart.md new file mode 100644 index 0000000..d1464a2 --- /dev/null +++ b/study_guide/Extension Methods in Dart.md @@ -0,0 +1,100 @@ +# Extension Methods in Dart: A Comprehensive Guide + +Extension methods allow you to add new functionality to existing libraries or classes without modifying their source code. + +## 1. Creating an Extension + +To create an extension, use the `extension` keyword followed by the extension name and the type you're extending. + +### Example: + +```dart +extension StringExtension on String { + String capitalize() { + return '${this[0].toUpperCase()}${this.substring(1)}'; + } +} +``` + +## 2. Using an Extension + +Once defined, you can use the extension method as if it were a part of the original class. + +### Example: + +```dart +void main() { + String name = 'john'; + print(name.capitalize()); // Prints: John +} +``` + +## 3. Extension on Built-in Types + +You can add extensions to any type, including built-in types like `int` or `List`. + +### Example: + +```dart +extension IntExtension on int { + bool isEven() => this % 2 == 0; +} + +extension ListExtension on List { + T getRandomElement() => this[DateTime.now().microsecond % length]; +} + +void main() { + print(42.isEven()); // Prints: true + + var fruits = ['apple', 'banana', 'orange']; + print(fruits.getRandomElement()); // Prints a random fruit +} +``` + +## 4. Naming Conflicts + +If there's a naming conflict, you can use the `hide` keyword to resolve it. + +### Example: + +```dart +extension MyStringExtension on String { + bool isLong() => length > 10; +} + +extension AnotherStringExtension on String { + bool isLong() => length > 20; +} + +void main() { + String text = 'Hello, World!'; + + // Use specific extension + print(MyStringExtension(text).isLong()); // Prints: true + print(AnotherStringExtension(text).isLong()); // Prints: false + + // Or hide one extension + import 'my_extensions.dart' hide MyStringExtension; +} +``` + +## 5. Static Extension Methods + +You can also define static extension methods. + +### Example: + +```dart +extension NumberParsing on String { + static int parseInt(String input) => int.parse(input); +} + +void main() { + print(NumberParsing.parseInt('42')); // Prints: 42 +} +``` + +## Conclusion + +Extension methods provide a clean way to add functionality to existing types without modifying their source code. They are particularly useful when working with types from external libraries or core Dart types that you can't modify directly. \ No newline at end of file diff --git a/study_guide/Flutter Animations.md b/study_guide/Flutter Animations.md new file mode 100644 index 0000000..0c75554 --- /dev/null +++ b/study_guide/Flutter Animations.md @@ -0,0 +1,206 @@ +# Flutter Animations: A Comprehensive Guide + +Animations add life to your Flutter applications, making them more engaging and intuitive. This guide covers implicit animations, explicit animations, and custom animations using AnimationController. + +## 1. Implicit Animations + +Implicit animations are the easiest way to add animations to your app. Flutter provides several implicitly animated widgets that automatically animate changes to their properties. + +### Example: AnimatedContainer + +```dart +import 'package:flutter/material.dart'; + +class ImplicitAnimationExample extends StatefulWidget { + @override + _ImplicitAnimationExampleState createState() => _ImplicitAnimationExampleState(); +} + +class _ImplicitAnimationExampleState extends State { + bool _isBig = false; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + setState(() { + _isBig = !_isBig; + }); + }, + child: AnimatedContainer( + duration: Duration(seconds: 1), + curve: Curves.easeInOut, + width: _isBig ? 300 : 100, + height: _isBig ? 300 : 100, + color: _isBig ? Colors.blue : Colors.red, + ), + ); + } +} +``` + +## 2. Explicit Animations + +Explicit animations give you more control over the animation process. They use an AnimationController to drive the animation. + +### Example: AnimatedBuilder + +```dart +import 'package:flutter/material.dart'; + +class ExplicitAnimationExample extends StatefulWidget { + @override + _ExplicitAnimationExampleState createState() => _ExplicitAnimationExampleState(); +} + +class _ExplicitAnimationExampleState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + )..repeat(reverse: true); + _animation = CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animation, + builder: (BuildContext context, Widget? child) { + return Container( + width: 200 * _animation.value, + height: 200 * _animation.value, + color: Colors.blue, + ); + }, + ); + } +} +``` + +## 3. Custom Animations with AnimationController + +For more complex animations, you can use AnimationController directly. + +### Example: Custom Animation + +```dart +import 'package:flutter/material.dart'; +import 'dart:math' as math; + +class CustomAnimationExample extends StatefulWidget { + @override + _CustomAnimationExampleState createState() => _CustomAnimationExampleState(); +} + +class _CustomAnimationExampleState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(seconds: 10), + vsync: this, + )..repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + builder: (_, child) { + return Transform.rotate( + angle: _controller.value * 2 * math.pi, + child: child, + ); + }, + child: Container( + width: 200, + height: 200, + color: Colors.green, + child: Center( + child: Text('Rotating!'), + ), + ), + ); + } +} +``` + +## 4. Hero Animations + +Hero animations create a visual connection between two screens, making transitions feel smooth and connected. + +### Example: Hero Animation + +```dart +import 'package:flutter/material.dart'; + +class HeroAnimationExample extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Hero Animation')), + body: GestureDetector( + onTap: () { + Navigator.push(context, MaterialPageRoute(builder: (_) { + return DetailScreen(); + })); + }, + child: Hero( + tag: 'imageHero', + child: Image.network( + 'https://picsum.photos/250?image=9', + ), + ), + ), + ); + } +} + +class DetailScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + body: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: Hero( + tag: 'imageHero', + child: Image.network( + 'https://picsum.photos/250?image=9', + ), + ), + ), + ), + ); + } +} +``` + +## Conclusion + +Animations are a powerful tool in Flutter for creating engaging and intuitive user interfaces. By mastering implicit animations, explicit animations, custom animations with AnimationController, and hero animations, you can create rich, interactive experiences in your Flutter applications. \ No newline at end of file diff --git a/study_guide/Flutter Navigation and Routing.md b/study_guide/Flutter Navigation and Routing.md new file mode 100644 index 0000000..0e51b44 --- /dev/null +++ b/study_guide/Flutter Navigation and Routing.md @@ -0,0 +1,267 @@ +# Flutter Navigation and Routing: A Comprehensive Guide + +Navigation and routing are essential for creating multi-screen applications in Flutter. This guide covers both Navigator 1.0 and 2.0 approaches, as well as named routes and generated routes. + +## 1. Navigator 1.0 + +Navigator 1.0 is the traditional way of handling navigation in Flutter. + +### Basic Navigation + +```dart +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: FirstScreen(), + ); + } +} + +class FirstScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('First Screen')), + body: Center( + child: ElevatedButton( + child: Text('Go to Second Screen'), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => SecondScreen()), + ); + }, + ), + ), + ); + } +} + +class SecondScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Second Screen')), + body: Center( + child: ElevatedButton( + child: Text('Go back'), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } +} +``` + +### Named Routes + +```dart +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + initialRoute: '/', + routes: { + '/': (context) => HomeScreen(), + '/details': (context) => DetailsScreen(), + }, + ); + } +} + +class HomeScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Home')), + body: Center( + child: ElevatedButton( + child: Text('Go to Details'), + onPressed: () { + Navigator.pushNamed(context, '/details'); + }, + ), + ), + ); + } +} + +class DetailsScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Details')), + body: Center( + child: ElevatedButton( + child: Text('Go back'), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } +} +``` + +## 2. Navigator 2.0 + +Navigator 2.0 provides a more declarative approach to navigation. + +### Example: + +```dart +import 'package:flutter/material.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + _MyAppState createState() => _MyAppState(); +} + +class _MyAppState extends State { + final RouteInformationParser> _routeInformationParser = MyRouteInformationParser(); + final RouterDelegate> _routerDelegate = MyRouterDelegate(); + + @override + Widget build(BuildContext context) { + return MaterialApp.router( + title: 'Navigator 2.0 Demo', + routerDelegate: _routerDelegate, + routeInformationParser: _routeInformationParser, + ); + } +} + +class MyRouteInformationParser extends RouteInformationParser> { + @override + Future> parseRouteInformation(RouteInformation routeInformation) async { + final uri = Uri.parse(routeInformation.location!); + return uri.pathSegments; + } + + @override + RouteInformation restoreRouteInformation(List configuration) { + return RouteInformation(location: '/' + configuration.join('/')); + } +} + +class MyRouterDelegate extends RouterDelegate> + with ChangeNotifier, PopNavigatorRouterDelegateMixin> { + final List _stack = ['']; + @override + final navigatorKey = GlobalKey(); + + List get stack => List.unmodifiable(_stack); + + @override + Widget build(BuildContext context) { + return Navigator( + key: navigatorKey, + pages: [ + MaterialPage( + key: ValueKey('HomePage'), + child: HomePage( + onPush: (String value) { + _stack.add(value); + notifyListeners(); + }, + ), + ), + if (_stack.length > 1) + MaterialPage( + key: ValueKey('DetailsPage'), + child: DetailsPage( + data: _stack.last, + onPop: () { + _stack.removeLast(); + notifyListeners(); + }, + ), + ), + ], + onPopPage: (route, result) { + if (!route.didPop(result)) { + return false; + } + if (_stack.length > 1) { + _stack.removeLast(); + notifyListeners(); + } + return true; + }, + ); + } + + @override + Future setNewRoutePath(List configuration) async { + _stack + ..clear() + ..addAll(configuration); + notifyListeners(); + } +} + +class HomePage extends StatelessWidget { + final void Function(String) onPush; + + HomePage({required this.onPush}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Home')), + body: Center( + child: ElevatedButton( + child: Text('Go to Details'), + onPressed: () => onPush('details'), + ), + ), + ); + } +} + +class DetailsPage extends StatelessWidget { + final String data; + final VoidCallback onPop; + + DetailsPage({required this.data, required this.onPop}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text('Details')), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Details for: $data'), + ElevatedButton( + child: Text('Go back'), + onPressed: onPop, + ), + ], + ), + ), + ); + } +} +``` + +## Conclusion + +Understanding both Navigator 1.0 and 2.0 approaches is crucial for effective navigation in Flutter applications. While Navigator 1.0 is simpler and sufficient for many apps, Navigator 2.0 offers more control and is especially useful for web applications and complex navigation scenarios. \ No newline at end of file diff --git a/study_guide/Flutter Widget Lifecycle.md b/study_guide/Flutter Widget Lifecycle.md new file mode 100644 index 0000000..5f5fb2c --- /dev/null +++ b/study_guide/Flutter Widget Lifecycle.md @@ -0,0 +1,159 @@ +# Flutter Widget Lifecycle: A Comprehensive Guide + +Understanding the lifecycle of widgets in Flutter is crucial for managing state and resources effectively. This guide covers the lifecycle of stateful widgets and the importance of keys. + +## 1. Stateful Widget Lifecycle + +Stateful widgets go through several lifecycle methods. Here's the order of execution: + +1. `createState()` +2. `initState()` +3. `didChangeDependencies()` +4. `build()` +5. `didUpdateWidget()` +6. `setState()` +7. `deactivate()` +8. `dispose()` + +### Example: + +```dart +import 'package:flutter/material.dart'; + +class LifecycleWidget extends StatefulWidget { + final String name; + + LifecycleWidget({Key? key, required this.name}) : super(key: key); + + @override + _LifecycleWidgetState createState() => _LifecycleWidgetState(); +} + +class _LifecycleWidgetState extends State { + @override + void initState() { + super.initState(); + print('initState'); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + print('didChangeDependencies'); + } + + @override + void didUpdateWidget(LifecycleWidget oldWidget) { + super.didUpdateWidget(oldWidget); + print('didUpdateWidget'); + } + + @override + void setState(VoidCallback fn) { + super.setState(fn); + print('setState'); + } + + @override + void deactivate() { + print('deactivate'); + super.deactivate(); + } + + @override + void dispose() { + print('dispose'); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + print('build'); + return Text(widget.name); + } +} +``` + +## 2. Explanation of Lifecycle Methods + +- `createState()`: Called when the StatefulWidget is inserted into the widget tree. +- `initState()`: Called once when the widget is inserted into the widget tree. +- `didChangeDependencies()`: Called when a dependency of this State object changes. +- `build()`: Called every time the widget needs to be rebuilt. +- `didUpdateWidget()`: Called whenever the widget configuration changes. +- `setState()`: Called when the internal state of the widget changes. +- `deactivate()`: Called when the widget is removed from the tree. +- `dispose()`: Called when the widget is removed permanently from the widget tree. + +## 3. Keys in Flutter + +Keys are used to preserve state when widgets move around in the widget tree. + +### Example: + +```dart +import 'package:flutter/material.dart'; + +class KeyExample extends StatefulWidget { + @override + _KeyExampleState createState() => _KeyExampleState(); +} + +class _KeyExampleState extends State { + List tiles = [ + StatefulColorfulTile(key: UniqueKey()), + StatefulColorfulTile(key: UniqueKey()), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row(children: tiles), + floatingActionButton: FloatingActionButton( + child: Icon(Icons.swap_horiz), + onPressed: swapTiles, + ), + ); + } + + swapTiles() { + setState(() { + tiles.insert(1, tiles.removeAt(0)); + }); + } +} + +class StatefulColorfulTile extends StatefulWidget { + StatefulColorfulTile({Key? key}) : super(key: key); + + @override + _StatefulColorfulTileState createState() => _StatefulColorfulTileState(); +} + +class _StatefulColorfulTileState extends State { + late Color myColor; + + @override + void initState() { + super.initState(); + myColor = Colors.primaries[ + DateTime.now().microsecondsSinceEpoch % Colors.primaries.length]; + } + + @override + Widget build(BuildContext context) { + return Container( + color: myColor, + child: Padding( + padding: EdgeInsets.all(70.0), + ), + ); + } +} +``` + +In this example, using `UniqueKey()` ensures that each tile maintains its state (color) even when their positions are swapped. + +## Conclusion + +Understanding the widget lifecycle and proper use of keys is essential for managing state and optimizing performance in Flutter applications. By leveraging these concepts, you can create more efficient and predictable widget behaviors. \ No newline at end of file diff --git a/study_guide/Functional Programming Concepts in Dart.md b/study_guide/Functional Programming Concepts in Dart.md new file mode 100644 index 0000000..bf960f1 --- /dev/null +++ b/study_guide/Functional Programming Concepts in Dart.md @@ -0,0 +1,141 @@ +# Functional Programming Concepts in Dart: A Comprehensive Guide + +While Dart is primarily an object-oriented language, it also supports many functional programming concepts. This guide covers key functional programming features in Dart. + +## 1. Higher-Order Functions + +Higher-order functions are functions that can take other functions as parameters or return functions. + +### Example: + +```dart +void main() { + var numbers = [1, 2, 3, 4, 5]; + + // Using a higher-order function (map) + var doubled = numbers.map((n) => n * 2); + print(doubled); // (2, 4, 6, 8, 10) + + // Creating a higher-order function + Function multiplyBy(int factor) { + return (int number) => number * factor; + } + + var tripler = multiplyBy(3); + print(tripler(4)); // 12 +} +``` + +## 2. Closures + +Closures are functions that have access to variables in their lexical scope, even when the function is used outside of its original scope. + +### Example: + +```dart +Function counter() { + int count = 0; + return () { + count++; + return count; + }; +} + +void main() { + var increment = counter(); + print(increment()); // 1 + print(increment()); // 2 + print(increment()); // 3 +} +``` + +## 3. Pure Functions + +Pure functions always produce the same output for the same input and have no side effects. + +### Example: + +```dart +// Pure function +int add(int a, int b) { + return a + b; +} + +// Impure function (has side effect) +int currentCount = 0; +int incrementAndAdd(int value) { + currentCount++; + return currentCount + value; +} + +void main() { + print(add(3, 4)); // Always 7 + print(incrementAndAdd(3)); // 4 + print(incrementAndAdd(3)); // 5 +} +``` + +## 4. Immutability + +Immutability involves working with unchangeable data. Dart supports this through `final` and `const` keywords. + +### Example: + +```dart +void main() { + final list = [1, 2, 3]; + // list = [4, 5, 6]; // Error: Can't assign to final variable + list.add(4); // OK, but modifies the list + + const constList = [1, 2, 3]; + // constList.add(4); // Error: Can't modify a const list + + // Creating a new list instead of modifying + final newList = [...list, 5]; + print(newList); // [1, 2, 3, 4, 5] +} +``` + +## 5. Recursion + +Recursion is a technique where a function calls itself to solve a problem. + +### Example: + +```dart +int factorial(int n) { + if (n <= 1) return 1; + return n * factorial(n - 1); +} + +void main() { + print(factorial(5)); // 120 +} +``` + +## 6. Function Composition + +Function composition involves creating a new function by combining other functions. + +### Example: + +```dart +Function compose(Function f, Function g) { + return (x) => f(g(x)); +} + +int square(int x) => x * x; +int addOne(int x) => x + 1; + +void main() { + var squareThenAddOne = compose(addOne, square); + var addOneThenSquare = compose(square, addOne); + + print(squareThenAddOne(3)); // 10 + print(addOneThenSquare(3)); // 16 +} +``` + +## Conclusion + +While Dart is not a purely functional language, it provides many features that support functional programming paradigms. Understanding and using these concepts can lead to more concise, maintainable, and testable code. \ No newline at end of file diff --git a/study_guide/Generics in Dart.md b/study_guide/Generics in Dart.md new file mode 100644 index 0000000..0d070ee --- /dev/null +++ b/study_guide/Generics in Dart.md @@ -0,0 +1,104 @@ +# Generics in Dart: A Comprehensive Guide + +Generics allow you to write flexible, reusable code that can work with different types while maintaining type safety. + +## 1. Generic Classes + +You can create classes that work with different types by using type parameters. + +### Example: + +```dart +class Box { + T contents; + + Box(this.contents); + + T getContents() { + return contents; + } +} + +void main() { + var intBox = Box(42); + var stringBox = Box("Hello"); + + print(intBox.getContents()); // Prints 42 + print(stringBox.getContents()); // Prints Hello +} +``` + +## 2. Generic Methods + +Methods can also use generics to work with different types. + +### Example: + +```dart +T first(List list) { + if (list.isEmpty) { + throw StateError("List is empty"); + } + return list[0]; +} + +void main() { + var numbers = [1, 2, 3]; + var strings = ["a", "b", "c"]; + + print(first(numbers)); // Prints 1 + print(first(strings)); // Prints a +} +``` + +## 3. Type Bounds + +You can restrict the types that can be used with a generic by using type bounds. + +### Example: + +```dart +class NumberBox { + T value; + + NumberBox(this.value); + + void square() { + print(value * value); + } +} + +void main() { + var intBox = NumberBox(5); + var doubleBox = NumberBox(2.5); + + intBox.square(); // Prints 25 + doubleBox.square(); // Prints 6.25 + + // var stringBox = NumberBox("Hello"); // Compile-time error +} +``` + +## 4. Generic Collections + +Dart's collection types are all generic. + +### Example: + +```dart +void main() { + List numbers = [1, 2, 3]; + Map ages = {"Alice": 30, "Bob": 25}; + Set scores = {9.5, 8.7, 9.2}; + + numbers.add(4); // OK + // numbers.add("5"); // Compile-time error + + print(ages["Alice"]); // Prints 30 + print(scores.contains(9.2)); // Prints true +} +``` + +## Conclusion + +Generics in Dart provide a powerful way to write reusable, type-safe code. They are extensively used in Dart's core libraries and are a fundamental concept for writing efficient and flexible Dart and Flutter applications. \ No newline at end of file diff --git a/study_guide/Mixins in Dart.md b/study_guide/Mixins in Dart.md new file mode 100644 index 0000000..ce5760a --- /dev/null +++ b/study_guide/Mixins in Dart.md @@ -0,0 +1,98 @@ +# Mixins in Dart: A Comprehensive Guide + +Mixins are a way of reusing a class's code in multiple class hierarchies. They allow you to add features to a class without inheritance. + +## 1. Creating a Mixin + +To create a mixin, use the `mixin` keyword followed by the mixin name. + +### Example: + +```dart +mixin Logger { + void log(String message) { + print('Log: $message'); + } +} +``` + +## 2. Using a Mixin + +To use a mixin, use the `with` keyword followed by the mixin name. + +### Example: + +```dart +class User with Logger { + String name; + + User(this.name); + + void greet() { + log('Hello, $name!'); + } +} + +void main() { + var user = User('Alice'); + user.greet(); // Prints: Log: Hello, Alice! +} +``` + +## 3. Multiple Mixins + +A class can use multiple mixins. + +### Example: + +```dart +mixin Swimmer { + void swim() => print('Swimming'); +} + +mixin Flyer { + void fly() => print('Flying'); +} + +class Duck with Swimmer, Flyer {} + +void main() { + var duck = Duck(); + duck.swim(); // Prints: Swimming + duck.fly(); // Prints: Flying +} +``` + +## 4. Mixin Inheritance + +Mixins can inherit from other mixins using the `on` keyword. + +### Example: + +```dart +mixin MusicalPerformer on Musician { + void perform() { + playInstrument(); + sing(); + } +} + +class Musician { + void playInstrument() => print('Playing instrument'); + void sing() => print('Singing'); +} + +class Singer extends Musician with MusicalPerformer {} + +void main() { + var singer = Singer(); + singer.perform(); + // Prints: + // Playing instrument + // Singing +} +``` + +## Conclusion + +Mixins provide a powerful way to reuse code across different class hierarchies. They are particularly useful for adding behavior to classes without the complexity of multiple inheritance. \ No newline at end of file diff --git a/study_guide/Null Safety in Dart.md b/study_guide/Null Safety in Dart.md new file mode 100644 index 0000000..debb58c --- /dev/null +++ b/study_guide/Null Safety in Dart.md @@ -0,0 +1,82 @@ +# Null Safety in Dart: A Comprehensive Guide + +Null safety is a feature in Dart that helps prevent null reference exceptions, making your code more robust and less prone to runtime errors. + +## 1. Nullable and Non-nullable Types + +In Dart with null safety, types are non-nullable by default. To make a type nullable, add a question mark (?) after the type. + +### Example: + +```dart +String nonNullableString = "Hello"; // Can't be null +String? nullableString = null; // Can be null + +int nonNullableInt = 42; // Can't be null +int? nullableInt = null; // Can be null +``` + +## 2. The `?`, `!`, and `??` Operators + +- `?`: Null-aware operator +- `!`: Null assertion operator +- `??`: Null-coalescing operator + +### Example: + +```dart +String? nullableString = null; + +// Null-aware operator +print(nullableString?.length); // Prints null + +// Null assertion operator (use with caution!) +// print(nullableString!.length); // Throws exception if nullableString is null + +// Null-coalescing operator +String nonNullString = nullableString ?? "Default"; +print(nonNullString); // Prints "Default" +``` + +## 3. Late Variables + +The `late` keyword allows you to declare a non-nullable variable that's initialized after its declaration. + +### Example: + +```dart +class Person { + late String name; + + void setName(String newName) { + name = newName; + } +} + +void main() { + final person = Person(); + person.setName("Alice"); + print(person.name); // Prints "Alice" +} +``` + +## 4. Required Named Parameters + +Use the `required` keyword to make named parameters non-nullable and required. + +### Example: + +```dart +void printPersonInfo({required String name, required int age}) { + print("Name: $name, Age: $age"); +} + +void main() { + printPersonInfo(name: "Bob", age: 30); // OK + // printPersonInfo(name: "Charlie"); // Compile-time error: missing required parameter 'age' +} +``` + +## Conclusion + +Null safety helps catch null-related errors at compile-time rather than runtime, leading to more reliable code. Practice using these features to write more robust Dart and Flutter applications. \ No newline at end of file diff --git a/study_guide/State Management in Flutter.md b/study_guide/State Management in Flutter.md new file mode 100644 index 0000000..3ae9356 --- /dev/null +++ b/study_guide/State Management in Flutter.md @@ -0,0 +1,159 @@ +# State Management in Flutter: A Comprehensive Guide + +State management is a crucial aspect of Flutter development. It involves managing the data and UI state of your application efficiently. This guide covers some popular state management approaches in Flutter. + +## 1. Provider + +Provider is a wrapper around InheritedWidget to make them easier to use and more reusable. + +### Example: + +```dart +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class CounterModel extends ChangeNotifier { + int _count = 0; + int get count => _count; + + void increment() { + _count++; + notifyListeners(); + } +} + +void main() { + runApp( + ChangeNotifierProvider( + create: (context) => CounterModel(), + child: MyApp(), + ), + ); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text('Provider Example')), + body: Center( + child: Consumer( + builder: (context, counter, child) => Text( + 'Count: ${counter.count}', + style: TextStyle(fontSize: 24), + ), + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => Provider.of(context, listen: false).increment(), + child: Icon(Icons.add), + ), + ), + ); + } +} +``` + +## 2. Riverpod + +Riverpod is a complete rewrite of Provider to make it more type-safe and able to catch programming errors at compile time. + +### Example: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final counterProvider = StateNotifierProvider((ref) => Counter()); + +class Counter extends StateNotifier { + Counter() : super(0); + void increment() => state++; +} + +void main() { + runApp(ProviderScope(child: MyApp())); +} + +class MyApp extends ConsumerWidget { + @override + Widget build(BuildContext context, WidgetRef ref) { + final count = ref.watch(counterProvider); + + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text('Riverpod Example')), + body: Center( + child: Text( + 'Count: $count', + style: TextStyle(fontSize: 24), + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => ref.read(counterProvider.notifier).increment(), + child: Icon(Icons.add), + ), + ), + ); + } +} +``` + +## 3. BLoC (Business Logic Component) + +BLoC separates the business logic from the UI, making the code more testable and reusable. + +### Example: + +```dart +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +// Events +abstract class CounterEvent {} +class IncrementEvent extends CounterEvent {} + +// BLoC +class CounterBloc extends Bloc { + CounterBloc() : super(0) { + on((event, emit) => emit(state + 1)); + } +} + +void main() { + runApp(BlocProvider( + create: (context) => CounterBloc(), + child: MyApp(), + )); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: Text('BLoC Example')), + body: Center( + child: BlocBuilder( + builder: (context, count) { + return Text( + 'Count: $count', + style: TextStyle(fontSize: 24), + ); + }, + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => context.read().add(IncrementEvent()), + child: Icon(Icons.add), + ), + ), + ); + } +} +``` + +## Conclusion + +Choosing the right state management solution depends on your app's complexity and your team's preferences. Each approach has its strengths, and understanding these different methods will help you make informed decisions in your Flutter projects. \ No newline at end of file diff --git a/posts/class_constructors_in_dart_and_flutter_a_comprehensive_guide.md b/study_guide/class_constructors_in_dart_and_flutter_a_comprehensive_guide.md similarity index 100% rename from posts/class_constructors_in_dart_and_flutter_a_comprehensive_guide.md rename to study_guide/class_constructors_in_dart_and_flutter_a_comprehensive_guide.md diff --git a/posts/oop_concepts_dart_flutter_comprehensive_guide.md b/study_guide/oop_concepts_dart_flutter_comprehensive_guide.md similarity index 100% rename from posts/oop_concepts_dart_flutter_comprehensive_guide.md rename to study_guide/oop_concepts_dart_flutter_comprehensive_guide.md