diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml new file mode 100644 index 0000000..c9e8681 --- /dev/null +++ b/.github/workflows/flutter-ci.yml @@ -0,0 +1,42 @@ +# Flutter CI – runs on every push and pull request to main +# Used in this course to demonstrate GitHub Actions and automated testing. + +name: Flutter CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build-and-test: + runs-on: ubuntu-latest + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + channel: stable + cache: true + + - name: Get dependencies + working-directory: task_manager_app + run: flutter pub get + + - name: Analyze code + working-directory: task_manager_app + run: flutter analyze + + - name: Run tests + working-directory: task_manager_app + run: flutter test + + - name: Build app + working-directory: task_manager_app + run: flutter build apk --debug diff --git a/.gitignore b/.gitignore index 366d335..595da43 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ task_manager_app/.flutter-plugins-dependencies # OS .DS_Store Thumbs.db + +# Node / npm +package-lock.json diff --git a/CICD_FOR_STUDENTS.md b/CICD_FOR_STUDENTS.md new file mode 100644 index 0000000..866e7e5 --- /dev/null +++ b/CICD_FOR_STUDENTS.md @@ -0,0 +1,169 @@ +# Simple CI/CD Flow – For Teaching (Mobile App Development) + +This document explains the CI/CD pipeline in a way you can use directly with students. Use it as a lecture guide or share it with them as a handout. + +--- + +## 1. The Five-Step Flow (Explain It Like This) + +### 1️⃣ Developer Writes Code + +A developer writes code on their machine. + +**Examples:** + +- Add a new feature +- Fix a bug +- Improve the UI + +Then they **commit** the code. + +--- + +### 2️⃣ Code Is Pushed to the Repository + +The developer runs: + +```bash +git push +``` + +The code goes to a **remote repository** (e.g. GitHub). + +--- + +### 3️⃣ CI Pipeline Starts Automatically + +When code is pushed: + +1. **GitHub** detects the change +2. The **GitHub Actions** workflow starts + +This happens because of the file: + +``` +.github/workflows/flutter-ci.yml +``` + +No one has to click “Run” — it starts automatically. + +--- + +### 4️⃣ Automated Steps Run + +The pipeline runs steps like: + +- Install dependencies +- Run static analysis +- Run tests +- Build the app + +For our Flutter project that means: + +- `flutter pub get` +- `flutter analyze` +- `flutter test` +- `flutter build` + +**No human intervention required.** + +--- + +### 5️⃣ Results Are Reported + +The system shows: + +- ✅ **Build passed** +- ❌ **Build failed** + +If something breaks, developers see it immediately (e.g. in the GitHub **Actions** tab or on the pull request). + +--- + +## 2. CI vs CD (Explain This Clearly) + +Students often confuse these. Keep the distinction simple: + +### CI — Continuous Integration + +- **Means:** Automatically **build** and **test** code whenever developers push changes. +- **Purpose:** + - Detect bugs early + - Make sure the code always builds + +### CD — Continuous Delivery / Deployment + +- **Means:** Automatically **deliver** or **deploy** the application. +- **Examples:** + - Build APK / IPA + - Upload to a testing environment + - Release to the store + +For mobile apps, CD might use tools like **Fastlane**, **Expo EAS**, or store deployment pipelines. + +**In this course we focus on CI** (build + test). CD is the next step in real-world teams. + +--- + +## 3. One-Sentence Explanation (Students Remember This) + +You can tell students: + +> **“CI/CD is like a robot developer that checks our code every time we push changes.”** + +That analogy usually makes the idea click. + +--- + +## 4. Example Flow in This Course + +Use this in your lecture: + +1. **Student writes code** (e.g. a small change in the app or a test). +2. **Commit** the change. +3. **Push** to GitHub. +4. **GitHub Actions** runs our CI workflow. +5. **Tests** run. +6. **Build** is verified. +7. **Result** is shown in the **Actions** tab. + +--- + +## 5. Live Demo (Students Love This) + +During the lecture: + +1. Make a **small commit** (e.g. `docs: update README` or change one line in the app). +2. **Push** it. +3. Open the **Actions** tab on GitHub. +4. Show the pipeline running **live**. + +That moment usually makes CI/CD click instantly. + +--- + +## 6. Optional: What Big Companies Do (If Students Ask) + +Explain that in industry, pipelines often include: + +- **Build** +- **Test** +- **Security scan** +- **Code quality checks** +- **Deployment** +- **Monitoring** + +So CI/CD becomes the **backbone** of modern software engineering. Our course pipeline is a simplified version of the same idea. + +--- + +## Quick Reference + +| Term | Meaning in one line | +|------------|-----------------------------------------------------------| +| **CI** | Automatically build and test when code is pushed. | +| **CD** | Automatically deliver or deploy the app. | +| **Workflow** | The set of steps defined in `.github/workflows/`. | +| **Actions tab** | Where you see runs and results on GitHub. | + +For more detail on what runs in *this* repo, see **CI_EXPLANATION.md**. diff --git a/CI_EXPLANATION.md b/CI_EXPLANATION.md new file mode 100644 index 0000000..8d0a5ea --- /dev/null +++ b/CI_EXPLANATION.md @@ -0,0 +1,58 @@ +# CI (Continuous Integration) – Beginner Guide + +This file explains what CI is and how it works in this repository. + +## What is CI? + +**Continuous Integration (CI)** means we automatically run checks every time code changes. + +Instead of waiting until the end of the week (or the end of the project) to find problems, CI helps us find problems early. + +## What is GitHub Actions? + +**GitHub Actions** is a tool inside GitHub that runs automated workflows. + +Workflows are defined in YAML files inside: + +``` +.github/workflows/ +``` + +## What happens when you push code? + +When you: + +- push to `main`, or +- open/update a pull request (PR) targeting `main` + +GitHub Actions runs our **Flutter CI** workflow. + +In this repository, the workflow: + +1. Checks out the code +2. Installs Flutter +3. Runs `flutter pub get` +4. Runs `flutter analyze` +5. Runs `flutter test` +6. Attempts to build the app + +If all steps pass, the run is **green**. If any step fails, it is **red**. + +## Why builds and tests matter in real teams + +In real software teams: + +- many people change the same codebase +- mistakes happen (even to experienced developers) + +Automated tests and builds help teams: + +- catch errors before merging code +- keep the `main` branch stable +- release software with more confidence + +CI does not guarantee “no bugs”, but it catches many common problems early (broken builds, failed tests, analysis warnings/errors). + +--- + +**For lecturers:** A longer teaching guide with the full CI/CD flow, CI vs CD, and live-demo tips is in **CICD_FOR_STUDENTS.md**. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6a997a4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,73 @@ +# Contributing – Git workflow for this course + +This guide explains the **Git workflow** you should follow when working on this repository. We use a simple **feature-branch + pull request** workflow so everyone’s work stays organized and reviewable. + +## Workflow in short + +- Update your local `main` +- Create a feature branch +- Make small commits with clear messages +- Push your branch +- Open a pull request (PR) to `main` +- Ensure CI is green, then merge + +Avoid committing directly to `main` for shared work. + +## Create a feature branch + +Start from an up-to-date `main`: + +```bash +git checkout main +git pull origin main +``` + +Create and switch to a new branch: + +```bash +git checkout -b feature/add-login-screen +``` + +Branch naming ideas: + +- `feature/...` for new features +- `fix/...` for bug fixes +- `docs/...` for documentation + +## Commit message format + +Write commit messages that are easy to understand. + +**Format:** + +``` +Short summary (imperative, no period) + +Optional details: what changed and why. +``` + +Examples: + +- `Add login screen UI` +- `Fix validation on empty task title` +- `Add Flutter CI workflow` + +## Open a pull request + +Push your branch: + +```bash +git push -u origin feature/add-login-screen +``` + +Then on GitHub: + +- Open a **Pull Request** targeting `main` +- Add a clear title and short description +- Wait for **CI checks** to finish +- If something fails, fix it and push again (the PR updates automatically) + +## What to do if CI fails + +CI failures are normal while learning. Read the logs in the GitHub **Actions** tab (or in the PR checks), fix the issue locally, then commit and push again. + diff --git a/README.md b/README.md index e2f04a6..8c47fd5 100644 Binary files a/README.md and b/README.md differ diff --git a/docs/STUDENT_REPO_SETUP.md b/docs/STUDENT_REPO_SETUP.md new file mode 100644 index 0000000..087130f --- /dev/null +++ b/docs/STUDENT_REPO_SETUP.md @@ -0,0 +1,152 @@ +# Option 2: Separate Student Repo – Step-by-Step Guide + +This guide walks you through creating and maintaining a **student-only** repository that contains only what students need (no solution branch, no internal docs). + +**Files used:** This guide (`docs/STUDENT_REPO_SETUP.md`) and the sync script (`scripts/sync-student-repo.sh`). Run the script from the **course repo root** (the folder that contains `task_manager_app`, `week-guides`, `scripts`). + +--- + +## Important: avoid nested Git repos + +If your **course repo** lives inside another Git repo (e.g. `ICET/course_repo`), then putting the student folder at `../flutter-course-student` would place it **inside that same parent repo** (`ICET/flutter-course-student`). That can cause: + +- The parent repo seeing the student folder as untracked/modified +- Confusion when running `git status` in the parent +- The parent accidentally tracking the student repo’s files + +**Do one of the following:** + +1. **Recommended:** Put the student repo **outside** the parent repo. For example, if your layout is `ICET/course_repo`, create the student folder somewhere else: + ```bash + # Example: same level as ICET (not inside it) + ./scripts/sync-student-repo.sh ../../flutter-course-student + + # Or use an absolute path + ./scripts/sync-student-repo.sh ~/Documents/flutter-course-student + ``` + Then run `git init` and the rest of the steps inside that folder. No nesting. + +2. **If you keep it inside the parent repo:** Add the student folder to the **parent** repo’s `.gitignore` (e.g. in `ICET/.gitignore` add `flutter-course-student/`). Then the parent repo will ignore it and the two repos won’t interfere. + +--- + +## What students will get + +- **task_manager_app/** – Flutter app to run and edit +- **week-guides/week2.md** – Week 2 plan +- **week-guides/week2-guideline/** – Student Development Guide, GROUP_TASKS, CI pointer (**no** complete-solution folder) +- **week-guides/week2-group-activity/** – Session guide and 8 group task files +- **.github/workflows/flutter-ci.yml** – CI runs when they push +- **CONTRIBUTING.md**, **CI_EXPLANATION.md**, **CICD_FOR_STUDENTS.md** +- **README.md** – Project overview (you can use the existing one or a shorter student-facing version) +- **.gitignore** + +Students will **not** see: `week2-solution` branch, `complete-solution` folder, or any other internal branches/files. + +--- + +## Part 1: One-time setup (create the student repo) + +### Step 1: Create the new repo on GitHub + +1. Go to GitHub and click **New repository**. +2. Name it (e.g. **flutter-course-student** or **icet-week2-student**). +3. Choose **Public** (or Private if you prefer). +4. **Do not** add a README, .gitignore, or license (we’ll push content from your machine). +5. Click **Create repository**. +6. Copy the repo URL (e.g. `https://github.com/your-org/flutter-course-student.git`). + +### Step 2: Run the sync script (first time) + +From your **course repo** root (the one that contains `task_manager_app`, `week-guides`, etc.): + +```bash +# Pick a target folder OUTSIDE any other git repo (see "Important: avoid nested Git repos" above). +# Example: sibling of the parent of course_repo (e.g. ICET/course_repo -> use ../../flutter-course-student) +# Or use an absolute path: ~/Documents/flutter-course-student + +mkdir -p ../../flutter-course-student +chmod +x scripts/sync-student-repo.sh +./scripts/sync-student-repo.sh ../../flutter-course-student +``` + +If your course repo is **not** inside another repo, `../flutter-course-student` is fine. This copies all student-needed files into the target folder. + +### Step 3: Turn the copied folder into a Git repo and push + +```bash +cd ../../flutter-course-student # or whatever path you used in Step 2 + +git init +git add . +git commit -m "Initial student repo (Week 2 materials)" +git branch -M main +git remote add origin https://github.com/YOUR_ORG/flutter-course-student.git +git push -u origin main +``` + +Replace `YOUR_ORG/flutter-course-student` with your actual GitHub repo URL. + +### Step 4: Share with students + +Give students: + +- **Clone:** `git clone https://github.com/YOUR_ORG/flutter-course-student.git` +- **Run app:** `cd flutter-course-student/task_manager_app && flutter pub get && flutter run` +- Tell them to use **main** (or whatever branch you use) and to open PRs against that branch. + +--- + +## Part 2: When you update the course repo (sync again) + +Whenever you change the course repo (e.g. update week-guides, CONTRIBUTING, or task_manager_app) and want students to see the updates: + +1. From the **course repo** root, run (use the same target path you used in Part 1): + ```bash + ./scripts/sync-student-repo.sh ../../flutter-course-student + ``` + +2. Go to the student repo folder, commit and push: + ```bash + cd ../../flutter-course-student + git add . + git status # check what changed + git commit -m "Sync: update guidelines / app / docs" + git push origin main + ``` + +Students then `git pull` to get the latest. + +--- + +## What the script does + +- Copies **task_manager_app** (excluding `build/`, `.dart_tool/`, and other generated files). +- Copies **week-guides/week2.md**, **week-guides/week2-group-activity/**, and **week-guides/week2-guideline/** but **excludes** **week-guides/week2-guideline/complete-solution/**. +- Copies **.github/workflows/flutter-ci.yml**, **CONTRIBUTING.md**, **CI_EXPLANATION.md**, **CICD_FOR_STUDENTS.md**, **README.md**, **.gitignore**. +- Does **not** copy: `.git`, `week2-solution` content, `complete-solution`, or other internal-only files. + +--- + +## Optional: Student-facing README + +If you want a shorter README just for students, after the first sync: + +1. Edit `../flutter-course-student/README.md` to say something like: “Flutter course – student materials. Clone, then run `task_manager_app` (see README in that folder). Use week-guides for instructions.” +2. Commit and push from the student repo folder. + +You can also create that file in the course repo in a folder that the script copies (e.g. a dedicated `README_STUDENT.md` that the script copies as `README.md` into the student repo). That way each sync overwrites the student README from your course repo. + +--- + +## Summary + +| Step | Where | Action | +|------|--------|--------| +| 1 | GitHub | Create new repo (e.g. flutter-course-student) | +| 2 | Course repo | Run `./scripts/sync-student-repo.sh ../flutter-course-student` | +| 3 | Student folder | `git init`, `add`, `commit`, `remote add`, `push` | +| 4 | — | Share clone URL with students | +| Later | Course repo | Run script again; then in student folder: `add`, `commit`, `push` | + +If anything in the script paths or names doesn’t match your repo (e.g. different branch name or folder names), edit **scripts/sync-student-repo.sh** and adjust the paths and exclusions. diff --git a/scripts/create-student-repo.sh b/scripts/create-student-repo.sh new file mode 100755 index 0000000..bdb9446 --- /dev/null +++ b/scripts/create-student-repo.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +# One-shot: create the student repo inside ICET (sibling to course_repo), sync files, +# init git, commit, and optionally add remote and push. +# +# Run from COURSE REPO ROOT (e.g. /Users/.../ICET/course_repo): +# ./scripts/create-student-repo.sh +# ./scripts/create-student-repo.sh https://github.com/YOUR_ORG/flutter-course-student.git +# +# Result: ICET/flutter-course-student/ is created and filled. If ICET is a git repo, +# flutter-course-student/ is added to ICET/.gitignore so the two repos don't conflict. + +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SOURCE="$(cd "$SCRIPT_DIR/.." && pwd)" +TARGET="${SOURCE}/../flutter-course-student" +PARENT="$(cd "$SOURCE/.." && pwd)" + +echo "Course repo (source): $SOURCE" +echo "Student repo (target): $TARGET" +echo "" + +mkdir -p "$TARGET" + +# 1. Sync files (reuse existing script) +"$SCRIPT_DIR/sync-student-repo.sh" "$TARGET" + +# 2. If parent (ICET) is a git repo, ignore the student folder so it doesn't track it +if [ -d "$PARENT/.git" ]; then + GITIGNORE="$PARENT/.gitignore" + if [ -f "$GITIGNORE" ]; then + if grep -q '^flutter-course-student/$' "$GITIGNORE" 2>/dev/null; then + echo "ICET/.gitignore already ignores flutter-course-student/" + else + echo "" >> "$GITIGNORE" + echo "# Student repo (separate git repo)" >> "$GITIGNORE" + echo "flutter-course-student/" >> "$GITIGNORE" + echo "Added flutter-course-student/ to $GITIGNORE" + fi + else + echo "flutter-course-student/" >> "$GITIGNORE" + echo "Created $GITIGNORE and added flutter-course-student/" + fi + echo "" +fi + +# 3. Init git in student folder, commit +cd "$TARGET" +if [ -d .git ]; then + echo "Student folder already has .git; reusing. Adding and committing..." + git add . + git status + git commit -m "Sync: update student materials" || true +else + git init + git add . + git commit -m "Initial student repo (Week 2 materials)" + git branch -M main + echo "Git repo initialized and first commit done." +fi + +# 4. Optional: add remote and push +if [ -n "$1" ]; then + if git remote get-url origin 2>/dev/null; then + echo "Remote 'origin' already set. To change: git remote set-url origin " + else + git remote add origin "$1" + echo "Pushing to $1 ..." + git push -u origin main + echo "Done. Students can: git clone $1" + fi +else + echo "" + echo "To push to GitHub:" + echo " 1. Create a new repo on GitHub (e.g. flutter-course-student)." + echo " 2. Run:" + echo " cd $TARGET" + echo " git remote add origin https://github.com/YOUR_ORG/flutter-course-student.git" + echo " git push -u origin main" +fi diff --git a/scripts/sync-student-repo.sh b/scripts/sync-student-repo.sh new file mode 100755 index 0000000..dc0ddb1 --- /dev/null +++ b/scripts/sync-student-repo.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Sync student-needed files from the course repo into a separate student repo folder. +# Run from the COURSE REPO ROOT: ./scripts/sync-student-repo.sh +# Example: ./scripts/sync-student-repo.sh ../flutter-course-student + +set -e + +if [ -z "$1" ]; then + echo "Usage: $0 " + echo "Example: $0 ../flutter-course-student" + exit 1 +fi + +TARGET="$(cd "$1" && pwd)" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SOURCE="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo "Source (course repo): $SOURCE" +echo "Target (student repo): $TARGET" +echo "" + +# Create target structure +mkdir -p "$TARGET" +mkdir -p "$TARGET/.github/workflows" +mkdir -p "$TARGET/week-guides" + +# 1. task_manager_app (exclude build artifacts and generated files) +echo "Copying task_manager_app..." +rsync -a --delete \ + --exclude='build/' \ + --exclude='.dart_tool/' \ + --exclude='.packages' \ + --exclude='.flutter-plugins' \ + --exclude='.flutter-plugins-dependencies' \ + "$SOURCE/task_manager_app/" "$TARGET/task_manager_app/" 2>/dev/null || { + # Fallback if rsync not available (e.g. some Windows) + cp -R "$SOURCE/task_manager_app" "$TARGET/" + rm -rf "$TARGET/task_manager_app/build" "$TARGET/task_manager_app/.dart_tool" 2>/dev/null || true +} + +# 2. week-guides: week2.md +echo "Copying week-guides/week2.md..." +cp "$SOURCE/week-guides/week2.md" "$TARGET/week-guides/" + +# 3. week-guides: week2-group-activity (entire folder) +echo "Copying week-guides/week2-group-activity..." +cp -R "$SOURCE/week-guides/week2-group-activity" "$TARGET/week-guides/" + +# 4. week-guides: week2-guideline EXCLUDING complete-solution +echo "Copying week-guides/week2-guideline (without complete-solution)..." +mkdir -p "$TARGET/week-guides/week2-guideline" +for f in "$SOURCE/week-guides/week2-guideline"/*; do + [ -e "$f" ] || continue + name="$(basename "$f")" + if [ "$name" = "complete-solution" ]; then + echo " Skipping complete-solution/" + continue + fi + cp -R "$f" "$TARGET/week-guides/week2-guideline/" +done + +# 5. .github/workflows +echo "Copying .github/workflows..." +cp "$SOURCE/.github/workflows/flutter-ci.yml" "$TARGET/.github/workflows/" + +# 6. Root docs and .gitignore +echo "Copying root docs..." +for f in CONTRIBUTING.md CI_EXPLANATION.md CICD_FOR_STUDENTS.md README.md .gitignore; do + if [ -f "$SOURCE/$f" ]; then + cp "$SOURCE/$f" "$TARGET/" + fi +done + +echo "" +echo "Done. Student repo content is in: $TARGET" +echo "Next: cd $TARGET && git add . && git status" diff --git a/task_manager_app/lib/home_page.dart b/task_manager_app/lib/home_page.dart index 08e1fc0..ddbfd2a 100644 --- a/task_manager_app/lib/home_page.dart +++ b/task_manager_app/lib/home_page.dart @@ -26,7 +26,7 @@ class HomePage extends StatelessWidget { ), const SizedBox(height: 8), Text( - 'Week 1 – Your first Flutter app', + 'Week 2 – Your first Flutter app', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), diff --git a/task_manager_app/pubspec.lock b/task_manager_app/pubspec.lock index 9ce301c..96ed6f9 100644 --- a/task_manager_app/pubspec.lock +++ b/task_manager_app/pubspec.lock @@ -111,10 +111,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.18" material_color_utilities: dependency: transitive description: @@ -188,10 +188,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.9" vector_math: dependency: transitive description: diff --git a/task_manager_app/test/widget_test.dart b/task_manager_app/test/widget_test.dart index 22c06cd..b563787 100644 --- a/task_manager_app/test/widget_test.dart +++ b/task_manager_app/test/widget_test.dart @@ -1,30 +1,26 @@ -// This is a basic Flutter widget test. +// Simple Flutter widget test – demonstrates how widget tests work. // -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. +// Widget tests: +// 1. Build a widget (or the whole app) with tester.pumpWidget() +// 2. Use find to locate widgets (by text, icon, type, etc.) +// 3. Use expect with matchers like findsOneWidget, findsNothing +// 4. Optionally interact: tester.tap(), tester.enterText(), then pump() -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:task_manager_app/main.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. + testWidgets('App shows Task Manager title and welcome message', + (WidgetTester tester) async { await tester.pumpWidget(const TaskManagerApp()); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + // AppBar title from HomePage. + expect(find.text('Task Manager'), findsOneWidget); - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); + // Body text from HomePage. + expect(find.text('Welcome to Task Manager'), findsOneWidget); - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + // Other useful matchers: findsNothing, findsWidgets, findsNWidgets(2) }); } diff --git a/week-guides/docs/CICD.jpeg b/week-guides/docs/CICD.jpeg new file mode 100644 index 0000000..3376c9f Binary files /dev/null and b/week-guides/docs/CICD.jpeg differ diff --git a/week-guides/docs/CICD_PIPELINE_AWS.png b/week-guides/docs/CICD_PIPELINE_AWS.png new file mode 100644 index 0000000..3f143dd Binary files /dev/null and b/week-guides/docs/CICD_PIPELINE_AWS.png differ diff --git a/week-guides/docs/CICD_PIPENILE1.jpeg b/week-guides/docs/CICD_PIPENILE1.jpeg new file mode 100644 index 0000000..94cd747 Binary files /dev/null and b/week-guides/docs/CICD_PIPENILE1.jpeg differ diff --git a/week-guides/week2-group-activity/week2-group-1.md b/week-guides/week2-group-activity/week2-group-1.md new file mode 100644 index 0000000..6394527 --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-1.md @@ -0,0 +1,56 @@ +# Week 2 – Group 1 Task + +**Branch to create:** `group-1/welcome-text` + +--- + +## Your goal + +Change the **welcome message** on the home screen of the Task Manager app (the text that says “Welcome to Task Manager”) to something of your choice (e.g. “Welcome, Group 1!”). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-1/welcome-text + ``` + +3. Open `task_manager_app/lib/home_page.dart`. Find the line with `'Welcome to Task Manager'` and change it to your new text. + +4. Run the app to check: + ```bash + cd task_manager_app + flutter pub get + flutter run + ``` + +5. Run tests (they should still pass; the test looks for “Task Manager” in the app bar and your new welcome text might need a test update if you changed it a lot—see note below): + ```bash + flutter test + ``` + If the test fails because it still expects “Welcome to Task Manager”, either change the text back to that or update `test/widget_test.dart` to expect your new text. + +6. Commit and push: + ```bash + cd .. # if you were in task_manager_app + git add task_manager_app/lib/home_page.dart + git commit -m "Change welcome message (Group 1)" + git push -u origin group-1/welcome-text + ``` + +7. On GitHub, open a **Pull Request** from `group-1/welcome-text` to `week-2`. Watch the **Actions** tab until CI finishes (green = passed). + +--- + +## Done when + +- Your PR is open and CI has run (green or red—both are fine for learning). +- You can explain what branch you used and what the CI did. diff --git a/week-guides/week2-group-activity/week2-group-2.md b/week-guides/week2-group-activity/week2-group-2.md new file mode 100644 index 0000000..aa1732a --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-2.md @@ -0,0 +1,55 @@ +# Week 2 – Group 2 Task + +**Branch to create:** `group-2/subtitle-text` + +--- + +## Your goal + +Change the **subtitle** on the home screen (the line that says “Week 2 – Your first Flutter app”) to something of your choice (e.g. “Week 2 – Group 2”). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-2/subtitle-text + ``` + +3. Open `task_manager_app/lib/home_page.dart`. Find the line with `'Week 2 – Your first Flutter app'` and change it to your new subtitle. + +4. Run the app to check: + ```bash + cd task_manager_app + flutter pub get + flutter run + ``` + +5. Run tests. If the test checks for this exact text, update `test/widget_test.dart` to expect your new subtitle, or keep the subtitle as “Week 2 – Your first Flutter app” and only change it slightly (e.g. add “ – Group 2”): + ```bash + flutter test + ``` + +6. Commit and push: + ```bash + cd .. + git add task_manager_app/lib/home_page.dart + git commit -m "Change subtitle (Group 2)" + git push -u origin group-2/subtitle-text + ``` + +7. On GitHub, open a **Pull Request** from `group-2/subtitle-text` to `week-2`. Watch the **Actions** tab until CI finishes. + +--- + +## Done when + +- Your PR is open and CI has run. +- You can explain what you changed and what the CI did. diff --git a/week-guides/week2-group-activity/week2-group-3.md b/week-guides/week2-group-activity/week2-group-3.md new file mode 100644 index 0000000..d3f1059 --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-3.md @@ -0,0 +1,54 @@ +# Week 2 – Group 3 Task + +**Branch to create:** `group-3/test-icon` + +--- + +## Your goal + +Add **one new assertion** in the widget test: check that the **check circle icon** is present on the home screen (the app uses `Icons.check_circle_outline`). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-3/test-icon + ``` + +3. Open `task_manager_app/test/widget_test.dart`. After the existing `expect(find.text('Welcome to Task Manager'), findsOneWidget);`, add: + ```dart + expect(find.byIcon(Icons.check_circle_outline), findsOneWidget); + ``` + You will need to add `import 'package:flutter/material.dart';` at the top if it is not already there. + +4. Run tests: + ```bash + cd task_manager_app + flutter pub get + flutter test + ``` + +5. Commit and push: + ```bash + cd .. + git add task_manager_app/test/widget_test.dart + git commit -m "Add test for check circle icon (Group 3)" + git push -u origin group-3/test-icon + ``` + +6. On GitHub, open a **Pull Request** from `group-3/test-icon` to `week-2`. Watch the **Actions** tab until CI finishes. + +--- + +## Done when + +- Your PR is open and CI has run (and passed, if nothing else is broken). +- You can explain what `find.byIcon` does and why the test passes. diff --git a/week-guides/week2-group-activity/week2-group-4.md b/week-guides/week2-group-activity/week2-group-4.md new file mode 100644 index 0000000..195b0e0 --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-4.md @@ -0,0 +1,54 @@ +# Week 2 – Group 4 Task + +**Branch to create:** `group-4/test-week2-text` + +--- + +## Your goal + +Add **one new assertion** in the widget test: check that the text **“Week 2”** appears somewhere on the home screen (the subtitle contains it). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-4/test-week2-text + ``` + +3. Open `task_manager_app/test/widget_test.dart`. After the existing expectations, add: + ```dart + expect(find.textContaining('Week 2'), findsOneWidget); + ``` + `find.textContaining('Week 2')` finds any widget whose text contains “Week 2”. + +4. Run tests: + ```bash + cd task_manager_app + flutter pub get + flutter test + ``` + +5. Commit and push: + ```bash + cd .. + git add task_manager_app/test/widget_test.dart + git commit -m "Add test for Week 2 text (Group 4)" + git push -u origin group-4/test-week2-text + ``` + +6. On GitHub, open a **Pull Request** from `group-4/test-week2-text` to `week-2`. Watch the **Actions** tab until CI finishes. + +--- + +## Done when + +- Your PR is open and CI has run (and passed). +- You can explain the difference between `find.text(...)` and `find.textContaining(...)`. diff --git a/week-guides/week2-group-activity/week2-group-5.md b/week-guides/week2-group-activity/week2-group-5.md new file mode 100644 index 0000000..0244beb --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-5.md @@ -0,0 +1,55 @@ +# Week 2 – Group 5 Task + +**Branch to create:** `group-5/appbar-title` + +--- + +## Your goal + +Change the **app bar title** from “Task Manager” to “Task Manager – Group 5” (or another title of your choice). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-5/appbar-title + ``` + +3. Open `task_manager_app/lib/home_page.dart`. Find the `AppBar` and change the `title` from `const Text('Task Manager')` to your new title (e.g. `const Text('Task Manager – Group 5')`). + +4. Run the app to check: + ```bash + cd task_manager_app + flutter pub get + flutter run + ``` + +5. Run tests. The current test expects `find.text('Task Manager')`. Update `test/widget_test.dart` to expect your new app bar text (e.g. `find.text('Task Manager – Group 5')`). + ```bash + flutter test + ``` + +6. Commit and push (include both files if you changed the test): + ```bash + cd .. + git add task_manager_app/lib/home_page.dart task_manager_app/test/widget_test.dart + git commit -m "Change app bar title (Group 5)" + git push -u origin group-5/appbar-title + ``` + +7. On GitHub, open a **Pull Request** from `group-5/appbar-title` to `week-2`. Watch the **Actions** tab until CI finishes. + +--- + +## Done when + +- Your PR is open and CI has run (and passed). +- You can explain why you had to update both the app and the test. diff --git a/week-guides/week2-group-activity/week2-group-6.md b/week-guides/week2-group-activity/week2-group-6.md new file mode 100644 index 0000000..815cf77 --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-6.md @@ -0,0 +1,54 @@ +# Week 2 – Group 6 Task + +**Branch to create:** `group-6/test-two-texts` + +--- + +## Your goal + +Add **two new assertions** in the widget test: check that there are **at least two** `Text` widgets on the screen (the home page has several text widgets). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-6/test-two-texts + ``` + +3. Open `task_manager_app/test/widget_test.dart`. Add an assertion that finds all widgets of type `Text` and checks there are at least 2: + ```dart + expect(find.byType(Text), findsAtLeastNWidgets(2)); + ``` + Add `import 'package:flutter/material.dart';` at the top if needed. + +4. Run tests: + ```bash + cd task_manager_app + flutter pub get + flutter test + ``` + +5. Commit and push: + ```bash + cd .. + git add task_manager_app/test/widget_test.dart + git commit -m "Add test for multiple Text widgets (Group 6)" + git push -u origin group-6/test-two-texts + ``` + +6. On GitHub, open a **Pull Request** from `group-6/test-two-texts` to `week-2`. Watch the **Actions** tab until CI finishes. + +--- + +## Done when + +- Your PR is open and CI has run (and passed). +- You can explain what `find.byType(Text)` and `findsAtLeastNWidgets(2)` do. diff --git a/week-guides/week2-group-activity/week2-group-7.md b/week-guides/week2-group-activity/week2-group-7.md new file mode 100644 index 0000000..a441ba4 --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-7.md @@ -0,0 +1,46 @@ +# Week 2 – Group 7 Task + +**Branch to create:** `group-7/readme-update` + +--- + +## Your goal + +Add **one line** to the main **README.md** at the root of the repository: a short sentence saying “Group 7 completed the Week 2 hands-on task.” (or similar). Add it in a sensible place (e.g. after the first paragraph or in a “Contributors” line). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-7/readme-update + ``` + +3. Open **README.md** (in the repo root, not inside `task_manager_app`). Add one line, for example: + ```markdown + Group 7 completed the Week 2 hands-on task. + ``` + Place it where it fits (e.g. after the bullet list of what the repo teaches, or at the end of the first section). + +4. Commit and push: + ```bash + git add README.md + git commit -m "docs: add Group 7 completion note (Week 2 task)" + git push -u origin group-7/readme-update + ``` + +5. On GitHub, open a **Pull Request** from `group-7/readme-update` to `week-2`. Watch the **Actions** tab until CI finishes (the Flutter CI will still run and should pass). + +--- + +## Done when + +- Your PR is open and CI has run. +- You can explain why CI still runs even though you only changed README (the workflow runs on every push to the branch and on every PR). diff --git a/week-guides/week2-group-activity/week2-group-8.md b/week-guides/week2-group-activity/week2-group-8.md new file mode 100644 index 0000000..89f7748 --- /dev/null +++ b/week-guides/week2-group-activity/week2-group-8.md @@ -0,0 +1,54 @@ +# Week 2 – Group 8 Task + +**Branch to create:** `group-8/test-scaffold` + +--- + +## Your goal + +Add **one new assertion** in the widget test: check that the screen has a **Scaffold** (the main layout widget used by the home page). + +--- + +## Steps + +1. Make sure you are on `week-2` and up to date: + ```bash + git checkout week-2 + git pull origin week-2 + ``` + +2. Create and switch to your group branch: + ```bash + git checkout -b group-8/test-scaffold + ``` + +3. Open `task_manager_app/test/widget_test.dart`. Add an assertion that finds a `Scaffold`: + ```dart + expect(find.byType(Scaffold), findsOneWidget); + ``` + Add `import 'package:flutter/material.dart';` at the top if it is not already there. + +4. Run tests: + ```bash + cd task_manager_app + flutter pub get + flutter test + ``` + +5. Commit and push: + ```bash + cd .. + git add task_manager_app/test/widget_test.dart + git commit -m "Add test for Scaffold (Group 8)" + git push -u origin group-8/test-scaffold + ``` + +6. On GitHub, open a **Pull Request** from `group-8/test-scaffold` to `week-2`. Watch the **Actions** tab until CI finishes. + +--- + +## Done when + +- Your PR is open and CI has run (and passed). +- You can explain what a Scaffold is and why it is useful to test for it. diff --git a/week-guides/week2-group-activity/week2-session-guide.md b/week-guides/week2-group-activity/week2-session-guide.md new file mode 100644 index 0000000..e0d362c --- /dev/null +++ b/week-guides/week2-group-activity/week2-session-guide.md @@ -0,0 +1,64 @@ +# Week 2 – Session Guide (1.5 hours, 8 groups) + +**Use this file to run the hands-on part of Week 2.** +Give each group the corresponding task file: **week2-group-1.md** through **week2-group-8.md**. + +--- + +## Aim + +Students practice the full flow: **branch → change code → commit → push → open PR → see CI run.** + +--- + +## Before the session + +- [ ] Repo is on branch `week-2` and pushed (students will clone or pull). +- [ ] Each group has (or will have) access to the same GitHub repo (e.g. fork, or one repo with branch-based tasks). +- [ ] Share the repo URL and confirm everyone has Flutter installed (`flutter doctor`). + +--- + +## Session outline (~1.5 hours) + +| Time | What happens | +|--------|----------------| +| **0:00** | Brief recap: Git workflow, PRs, CI (use **CICD_FOR_STUDENTS.md** if needed). | +| **0:05** | Assign groups: Group 1 → **week2-group-1.md**, Group 2 → **week2-group-2.md**, … Group 8 → **week2-group-8.md**. | +| **0:10** | Groups read their task and create their branch (e.g. `group-1/welcome-text`). | +| **0:15–0:50** | Hands-on: each group does its task (edit code/tests, commit, push, open PR). | +| **0:50** | Quick check: each group shows their PR and that CI is running or has passed. | +| **1:00–1:25** | Wrap-up: walk through one PR and the Actions tab; clarify CI vs CD; Q&A. | +| **1:25–1:30** | Optional: merge one or two PRs live to show the full flow. | + +--- + +## Group–file mapping + +| Group | Task file | Focus (short) | +|---------|----------------------|------------------------| +| Group 1 | week2-group-1.md | Change welcome message text | +| Group 2 | week2-group-2.md | Change subtitle text | +| Group 3 | week2-group-3.md | Add test: find icon | +| Group 4 | week2-group-4.md | Add test: find “Week 2” text | +| Group 5 | week2-group-5.md | Change app bar title | +| Group 6 | week2-group-6.md | Add test: two text widgets | +| Group 7 | week2-group-7.md | Update README one line | +| Group 8 | week2-group-8.md | Add test: Scaffold present | + +--- + +## Tips for the instructor + +1. **Branch names:** Each task suggests a branch name (e.g. `group-1/welcome-text`). This avoids clashes when all 8 groups push. +2. **One repo vs forks:** If all use the same repo, they need push access and must use different branch names (the task files already do this). +3. **CI:** Remind students to open a **Pull Request** (targeting `week-2` or `main`) so they can see the Actions run. +4. **Stuck groups:** Common issues – wrong branch, forgot `flutter pub get` or `flutter test` before pushing, or PR target branch wrong. Have them check CONTRIBUTING.md. +5. **Time:** If short on time, reduce to 4 groups (e.g. use tasks 1, 2, 3, 4 only) or let two groups share one task file. + +--- + +## After the session + +- You can merge the PRs later or leave them open for review. +- Remind students to read **CI_EXPLANATION.md** and **CICD_FOR_STUDENTS.md** for revision. diff --git a/week-guides/week2-group-activity2/README.md b/week-guides/week2-group-activity2/README.md new file mode 100644 index 0000000..386177a --- /dev/null +++ b/week-guides/week2-group-activity2/README.md @@ -0,0 +1,26 @@ +# Week 2 – Group Activity 2 (Students & Lecturers) + +Ready-to-use task documents for **8 groups**. Each group implements one feature and opens a Pull Request to the main branch. + +## Group–task mapping + +| Group | Task file | Feature | Branch name | +|---------|---------------------|----------------------|-----------------------------| +| Group 1 | week2-group-1.md | Student Registration | `feature/student-registration` | +| Group 2 | week2-group-2.md | Student List | `feature/student-list` | +| Group 3 | week2-group-3.md | Student Details | `feature/student-details` | +| Group 4 | week2-group-4.md | Register Lecturer | `feature/register-lecturer` | +| Group 5 | week2-group-5.md | Lecturer Details | `feature/lecturer-details` | +| Group 6 | week2-group-6.md | Lecturer List | `feature/lecturer-list` | +| Group 7 | week2-group-7.md | Dashboard Screen | `feature/dashboard` | +| Group 8 | week2-group-8.md | UI Improvements | `feature/ui-improvements` | + +## How to use + +1. Assign each group the corresponding **week2-group-X.md** file. +2. Groups create their branch (e.g. `feature/student-registration`), implement the feature, commit, push, and open a PR. +3. Target branch for PRs: `week-2` or `main` (as set by your repo). + +## Reference solution + +A complete reference solution (Students & Lecturers) lives in **week2-guideline/complete-solution/** on the **week2-solution** branch. Use it for demos or if a group is stuck. diff --git a/week-guides/week2-group-activity2/week2-group-1.md b/week-guides/week2-group-activity2/week2-group-1.md new file mode 100644 index 0000000..c777264 --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-1.md @@ -0,0 +1,28 @@ +# Group 1 – Student Registration + +## Feature + +Implement the **Student Registration Screen**. + +## Tasks + +- Create a form to register a new student. +- **Fields:** + - Student Name + - Student ID + - Email + - Course +- Add a **Submit** button. + +## Requirements + +- Use Flutter widgets for the UI. +- Validate that all fields are filled. + +## Expected Outcome + +Users should be able to add a new student to the system. + +## Branch Name + +`feature/student-registration` diff --git a/week-guides/week2-group-activity2/week2-group-2.md b/week-guides/week2-group-activity2/week2-group-2.md new file mode 100644 index 0000000..c3d0508 --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-2.md @@ -0,0 +1,22 @@ +# Group 2 – Student List + +## Feature + +Display the list of registered students. + +## Tasks + +- Create a screen to show all students. +- Use a **ListView** to display them. +- Each list item should show: + - Student Name + - Student ID + - Course + +## Expected Outcome + +Users should be able to see all students in a scrollable list. + +## Branch Name + +`feature/student-list` diff --git a/week-guides/week2-group-activity2/week2-group-3.md b/week-guides/week2-group-activity2/week2-group-3.md new file mode 100644 index 0000000..250617e --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-3.md @@ -0,0 +1,22 @@ +# Group 3 – Student Details + +## Feature + +Create a **Student Details Screen**. + +## Tasks + +- When a student item is clicked, open a details page. +- Display: + - Name + - ID + - Email + - Course + +## Expected Outcome + +Users should be able to view detailed information about a student. + +## Branch Name + +`feature/student-details` diff --git a/week-guides/week2-group-activity2/week2-group-4.md b/week-guides/week2-group-activity2/week2-group-4.md new file mode 100644 index 0000000..5b353f2 --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-4.md @@ -0,0 +1,28 @@ +# Group 4 – Register Lecturer + +## Feature + +Implement the **Lecturer Registration Screen**. + +## Tasks + +- Create a form to register a new lecturer. +- **Fields:** + - Lecturer Name + - Lecturer ID + - Email + - Department +- Add a **Submit** button. + +## Requirements + +- Use Flutter widgets for the UI. +- Validate that all fields are filled. + +## Expected Outcome + +Users should be able to add a new lecturer to the system. + +## Branch Name + +`feature/register-lecturer` diff --git a/week-guides/week2-group-activity2/week2-group-5.md b/week-guides/week2-group-activity2/week2-group-5.md new file mode 100644 index 0000000..4278bbd --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-5.md @@ -0,0 +1,22 @@ +# Group 5 – Lecturer Details + +## Feature + +Create a **Lecturer Details Screen**. + +## Tasks + +- When a lecturer item is clicked (in the lecturer list), open a details page. +- Display: + - Name + - ID + - Email + - Department + +## Expected Outcome + +Users should be able to view detailed information about a lecturer. + +## Branch Name + +`feature/lecturer-details` diff --git a/week-guides/week2-group-activity2/week2-group-6.md b/week-guides/week2-group-activity2/week2-group-6.md new file mode 100644 index 0000000..4b661cd --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-6.md @@ -0,0 +1,22 @@ +# Group 6 – Lecturer List + +## Feature + +Display the list of registered lecturers. + +## Tasks + +- Create a screen to show all lecturers. +- Use a **ListView** to display them. +- Each list item should show: + - Lecturer Name + - Lecturer ID + - Department + +## Expected Outcome + +Users should be able to see all lecturers in a scrollable list. + +## Branch Name + +`feature/lecturer-list` diff --git a/week-guides/week2-group-activity2/week2-group-7.md b/week-guides/week2-group-activity2/week2-group-7.md new file mode 100644 index 0000000..ac2689e --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-7.md @@ -0,0 +1,22 @@ +# Group 7 – Dashboard Screen + +## Feature + +Create a **Dashboard Screen** as the app’s main page. + +## Tasks + +- Display quick navigation to: + - Student List + - Add Student + - Lecturer List + - Add Lecturer +- Optionally show total number of students and lecturers (if data is available). + +## Expected Outcome + +A simple dashboard UI that acts as the app’s main page and links to student and lecturer sections. + +## Branch Name + +`feature/dashboard` diff --git a/week-guides/week2-group-activity2/week2-group-8.md b/week-guides/week2-group-activity2/week2-group-8.md new file mode 100644 index 0000000..e1b4df8 --- /dev/null +++ b/week-guides/week2-group-activity2/week2-group-8.md @@ -0,0 +1,19 @@ +# Group 8 – UI Improvements (Backup Group) + +## Feature + +Improve the **UI and user experience**. + +## Tasks + +- Improve layout and spacing. +- Add icons. +- Improve colors and styling. + +## Expected Outcome + +The app should look **clean and user friendly**. + +## Branch Name + +`feature/ui-improvements` diff --git a/week-guides/week2-guideline/CI_EXPLANATION.md b/week-guides/week2-guideline/CI_EXPLANATION.md new file mode 100644 index 0000000..51b4e6b --- /dev/null +++ b/week-guides/week2-guideline/CI_EXPLANATION.md @@ -0,0 +1,8 @@ +# CI (Continuous Integration) – Quick Reference + +A short pointer for students. Full explanation is in the repository root: + +- **../../CI_EXPLANATION.md** – What is CI? What is GitHub Actions? What runs on push/PR? +- **../../CICD_FOR_STUDENTS.md** – Full teaching guide: CI/CD flow, CI vs CD, live demo tips. + +In short: when you push code or open a Pull Request, GitHub Actions runs `flutter pub get`, `flutter analyze`, `flutter test`, and a build. Green = passed, red = fix and push again. diff --git a/week-guides/week2-guideline/GROUP_TASKS.md b/week-guides/week2-guideline/GROUP_TASKS.md new file mode 100644 index 0000000..4db313a --- /dev/null +++ b/week-guides/week2-guideline/GROUP_TASKS.md @@ -0,0 +1,8 @@ +# Week 2 – Group Tasks + +Group tasks for the 1.5‑hour hands-on session are in the **week2-group-activity** folder. + +- **Instructor:** Use **../week2-group-activity/week2-session-guide.md** for timing and tips. +- **Groups 1–8:** Use **../week2-group-activity/week2-group-1.md** through **week2-group-8.md** (one task per group). + +Each group: create a feature branch → make the change → commit → push → open a Pull Request → watch CI run. diff --git a/week-guides/week2-guideline/INTRODUCTION_TO_DART.md b/week-guides/week2-guideline/INTRODUCTION_TO_DART.md new file mode 100644 index 0000000..263c3ef --- /dev/null +++ b/week-guides/week2-guideline/INTRODUCTION_TO_DART.md @@ -0,0 +1,203 @@ +# Introduction to Dart – Basics for Flutter (Week 2) + +Use this note **at the very beginning** of the Flutter part of the session. It covers just enough Dart to read and write simple Flutter code: variables, types, functions, classes, and `const`. + +--- + +## 1. Variables and types + +Dart is **statically typed**: every variable has a type (you can omit it and use **type inference**). + +```dart +String name = 'Alice'; +int count = 42; +bool isActive = true; +double price = 9.99; +``` + +**Type inference** (no type written – Dart infers it): + +```dart +var message = 'Hello'; // Dart infers String +var number = 10; // Dart infers int +``` + +**`final`** – assign once, then cannot change: + +```dart +final title = 'Task Manager'; +``` + +--- + +## 2. Strings + +```dart +String a = 'Single quotes'; +String b = "Double quotes"; +String combined = 'Hello, $name'; // String interpolation +String expression = 'Result: ${1 + 2}'; // Any expression in ${ } +``` + +--- + +## 3. Functions + +**With return type and parameters:** + +```dart +int add(int a, int b) { + return a + b; +} +``` + +**Short form (arrow function) when the body is one expression:** + +```dart +int add(int a, int b) => a + b; +``` + +**No return value** – use **`void`**: + +```dart +void printMessage(String msg) { + print(msg); +} +``` + +**Named parameters** (very common in Flutter widgets): + +```dart +void showDialog({required String title, String? message}) { + // title is required; message is optional (can be null) +} +``` + +In Flutter you see this all the time: **`child: Text('Hi')`**, **`padding: EdgeInsets.all(16)`** – those are named parameters. + +--- + +## 4. Classes – the basics + +A **class** groups data and behaviour. **Constructor** names the class. + +```dart +class Student { + final String name; + final String id; + final String course; + + Student({ + required this.name, + required this.id, + required this.course, + }); +} +``` + +- **`required`** – caller must pass that argument. +- **`this.name`** – assigns the parameter to the field **name**. + +**Creating an instance:** + +```dart +var student = Student(name: 'Alice', id: 'S001', course: 'CS'); +``` + +In Flutter, **widgets are classes**: **`Text('Hello')`**, **`ElevatedButton(...)`** create instances of the **Text** and **ElevatedButton** classes. + +--- + +## 5. const – compile-time constants + +**`const`** means: “fixed at compile time.” Use it for values that never change. + +```dart +const int maxItems = 100; +const String appTitle = 'Task Manager'; +const padding = EdgeInsets.all(16); // Same object reused +``` + +**`const`** constructors (Flutter widgets): + +```dart +const Text('Home Page') +const EdgeInsets.all(16) +``` + +**Rule:** If a widget has **no callbacks** (no `onPressed`, no `onTap`) and all its arguments are constant, you can mark it **`const`**. If it has a callback or non-constant data, **don’t** use **`const`** on that widget. + +--- + +## 6. Null safety (brief) + +By default, variables **cannot** be **null** unless you say so. + +```dart +String name = 'Alice'; +name = null; // ❌ Error: String can't be null +``` + +**Nullable type** – add **`?`**: + +```dart +String? nickname = null; // OK +nickname = 'Al'; // OK +``` + +**Accessing nullable values:** use **`!`** only if you’re sure it’s not null, or **`?.`** for safe access: + +```dart +int? length = nickname?.length; // null if nickname is null +``` + +--- + +## 7. Collections – List and Map + +**List** – ordered sequence: + +```dart +List names = ['Alice', 'Bob', 'Carol']; +names.add('Dave'); +int count = names.length; +String first = names[0]; +``` + +**Map** – key–value pairs: + +```dart +Map ages = { + 'Alice': 20, + 'Bob': 22, +}; +int? aliceAge = ages['Alice']; +``` + +In Flutter, **`children: [ ... ]`** is a **List** of widgets. + +--- + +## 8. What you’ll see in Flutter code + +| Dart concept | Example in Flutter | +|-----------------|----------------------------------------| +| Class + constructor | `Scaffold(body: Column(...))` | +| Named parameters| `padding: EdgeInsets.all(16)` | +| const | `const Text('Hello')` | +| final | `final String title = 'App';` | +| List | `children: [Text('A'), Text('B')]` | +| Function/callback | `onPressed: () { Navigator.pop(context); }` | +| void | `void main() { ... }` | + +--- + +## 9. Order in the session + +Do this **Introduction to Dart** first (this note), then: + +1. **NAVIGATOR_NOTES.md** – push, pop, stack +2. **SCAFFOLD_AND_WIDGETS_NOTES.md** – Scaffold, child, children +3. **STUDENT_DEVELOPMENT_GUIDE.md** – screens, form, ListView + +See **LESSON_PLAN.md** for the full order and timings. diff --git a/week-guides/week2-guideline/LESSON_PLAN.md b/week-guides/week2-guideline/LESSON_PLAN.md new file mode 100644 index 0000000..c3db227 --- /dev/null +++ b/week-guides/week2-guideline/LESSON_PLAN.md @@ -0,0 +1,119 @@ +# Week 2 – Lesson Plan (Flutter + Navigator + Add Student UI) + +This **lesson plan** gives the recommended order for the session: **Dart basics first**, then **Navigator**, then **Add Student UI**. You can follow it while sharing the repo on screen. + +--- + +## Prerequisites + +- Repo is on branch **`week-2`**; run `flutter pub get` and `flutter run`. +- You should see the Task Manager app with **HomePage** (welcome text, no Student Management yet). + +--- + +## Phase 0: Introduction to Dart (very first) — ~10–15 min + +**Goal:** Enough Dart to read and write simple Flutter code (variables, types, functions, classes, `const`). + +- **Notes:** **INTRODUCTION_TO_DART.md**. +- Cover briefly: + - Variables and types (`String`, `int`, `bool`, `var`, `final`). + - Functions (return type, named parameters – as used in widgets). + - Classes and constructors (e.g. `Student`, and that widgets are classes like `Text(...)`). + - **`const`** – when to use it and when not (e.g. no `const` if there’s an `onPressed` callback). + - **List** – `children: [ ... ]` is a list of widgets. +- You don’t need to do every section in depth; focus on what they’ll see in **main.dart** and in widgets (named params, `const`, `child`/`children`). +- Then move to Phase 1 (Navigator). + +--- + +## Phase 1: Navigator — ~15–20 min + +**Goal:** Understand **stack**, **push**, and **pop** before building any new UI. + +### 1.1 Explain the stack (2–3 min) + +- Use **NAVIGATOR_NOTES.md** Section 1–4 (stack diagram, push, pop). +- "Screens are like a stack of cards. Push = new screen on top. Pop = remove top, go back." + +### 1.2 Minimal demo: second screen (10–12 min) + +- **Notes:** **NAVIGATOR_NOTES.md** Section 5 (Demo: minimal Navigator on Week 2 app). +- **Copy-paste:** **demo-snippets/navigator-demo/**: + 1. Create **`lib/second_screen.dart`** — paste from **`demo-snippets/navigator-demo/second_screen.dart`**. + 2. Edit **`lib/home_page.dart`** — add import and button from **`demo-snippets/navigator-demo/home_page_add_button.txt`** (or from NAVIGATOR_NOTES.md). +- Run the app: tap "Go to Second Screen" (push), then "Go Back" (pop). +- Emphasise: "The same **push** and **pop** will be used for Add Student." + +--- + +## Phase 2: Add Student UI creation flow — ~20–25 min + +**Goal:** Build the Add Student screen (form UI) and wire it with **push** from the home/dashboard and **pop** from the button. + +### 2.1 Screen structure (2–3 min) + +- **Notes:** **STUDENT_DEVELOPMENT_GUIDE.md** Section 2 (Creating a Simple UI Screen). +- Explain: **Scaffold** → AppBar + Body. Show a simple Scaffold example. + +### 2.2 Build Add Student screen (form UI) (10–12 min) + +- **Notes:** **STUDENT_DEVELOPMENT_GUIDE.md** Section 5 (Creating a Form – Add Student). +- **Copy-paste:** **demo-snippets/add-student-ui/add_student_screen.dart** (or full screen from **complete-solution**). +- Build step by step if preferred: + 1. New file **`lib/screens/add_student_screen.dart`**. + 2. Scaffold with AppBar title "Add Student". + 3. Body: **Padding** → **Column** with: + - TextField (Student Name) + - SizedBox(16) + - TextField (Student ID) + - SizedBox(16) + - TextField (Course) + - SizedBox(24) + - ElevatedButton "Add Student" → **onPressed: Navigator.pop(context)**. +- Emphasise: "The button **pops** so we go back — same as 'Go Back' in the Navigator demo." + +### 2.3 Wire from HomePage / Dashboard (5–8 min) + +- **Notes:** **STUDENT_DEVELOPMENT_GUIDE.md** Section 7 (Navigation Between Screens). +- If you have a dashboard: add a button that **Navigator.push** to **AddStudentScreen** (see **complete-solution/lib/screens/student_dashboard_screen.dart**). +- If using week-2’s HomePage only: add an "Add Student" button that pushes to **AddStudentScreen** (same pattern as "Go to Second Screen"). + +--- + +## Phase 3: Full solution (reference / copy-paste) — as needed + +- **Notes:** **complete-solution/README.md** (Order of Implementation). +- **When to use:** To show the full app (Dashboard + Student List + Add Student + routes in main.dart) or if a group is stuck. +- Paste order: **models/student.dart** → **screens/student_dashboard_screen.dart** → **screens/student_list_screen.dart** → **screens/add_student_screen.dart** → **main.dart**. + +--- + +## Quick reference: where to find what + +| What you need | File or folder | +|---------------|-----------------| +| **Dart basics** (variables, types, functions, classes, const) – do first | **INTRODUCTION_TO_DART.md** | +| Navigator theory + minimal demo code | **NAVIGATOR_NOTES.md** | +| Scaffold parameters, child vs children, layout pattern | **SCAFFOLD_AND_WIDGETS_NOTES.md** | +| Navigator demo copy-paste (second screen + HomePage button) | **demo-snippets/navigator-demo/** | +| Add Student UI (form) copy-paste | **demo-snippets/add-student-ui/** or **complete-solution/lib/screens/add_student_screen.dart** | +| General Flutter structure, ListView, form, navigation | **STUDENT_DEVELOPMENT_GUIDE.md** | +| Full app paste order | **complete-solution/README.md** | +| Week 2 overview (Git, CI, Flutter) | **../week2.md** | +| Session timing (groups, PRs, CI) | **../week2-group-activity/week2-session-guide.md** | + +--- + +## Suggested timeline (Flutter part of the session) + +| Time | Activity | +|------|----------| +| 0:00 | Recap Git/CI if needed; switch to Flutter. | +| 0:05 | **Phase 0:** Introduction to Dart (**INTRODUCTION_TO_DART.md**) – variables, types, functions, classes, const, List. | +| 0:18 | **Phase 1:** Navigator (stack, push, pop) + minimal demo (second_screen + HomePage button). | +| 0:35 | **Phase 2:** Add Student UI (Scaffold, form, pop); then wire from dashboard or HomePage. | +| 0:58 | **Phase 3 (optional):** Show full solution paste order or let groups continue. | +| 1:00+ | Group tasks (branch, change, PR, CI) as in **week2-session-guide.md**. | + +You can shorten Phase 0 or Phase 2 if needed; Phase 0 can be a quick “Dart in 5 min” if time is tight. diff --git a/week-guides/week2-guideline/NAVIGATOR_NOTES.md b/week-guides/week2-guideline/NAVIGATOR_NOTES.md new file mode 100644 index 0000000..aaa3301 --- /dev/null +++ b/week-guides/week2-guideline/NAVIGATOR_NOTES.md @@ -0,0 +1,227 @@ +# Navigator – Teaching Notes (Week 2) + +Use this document to **teach navigation first**, before building the Add Student UI. Students need to understand **push** and **pop** so that the "Add Student" button behaviour (going back) makes sense. + +--- + +## 1. Core idea: the stack + +Flutter keeps screens in a **stack** (like a stack of cards): + +- **Bottom** = first screen (e.g. Dashboard / Home). +- **Top** = current screen the user sees. + +``` + ┌─────────────────┐ + │ Add Student │ ← TOP (visible) + ├─────────────────┤ + │ Dashboard │ ← BOTTOM + └─────────────────┘ +``` + +- **Navigator.push** → put a **new** screen **on top** (user goes "forward"). +- **Navigator.pop** → **remove** the **top** screen (user goes "back"). + +--- + +## 2. Going to a new screen (push) + +To open another screen, use **`Navigator.push`** with a **`MaterialPageRoute`**: + +```dart +Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SecondScreen(), + ), +); +``` + +- **`context`** – needed so Flutter knows where we are in the app. +- **`MaterialPageRoute`** – builds the new screen and gives the default slide-from-right animation. +- **`builder`** – returns the widget (screen) to show. + +**Where to use this:** In the **onPressed** of a button on the **current** screen (e.g. "Add Student" on the Dashboard). + +--- + +## 3. Going back (pop) + +To close the **current** screen and show the previous one: + +```dart +Navigator.pop(context); +``` + +- Removes the **top** screen from the stack. +- The screen below becomes visible again. + +**Where to use this:** In the **onPressed** of a "Back" or "Add Student" or "Cancel" button on the **new** screen. + +--- + +## 4. Flow summary + +| Action | Code | Stack change | +|--------|------|--------------| +| User taps "Add Student" on Dashboard | `Navigator.push(context, MaterialPageRoute(...))` | [Dashboard] → [Dashboard, Add Student] | +| User taps "Add Student" (submit) or "Back" on Add Student screen | `Navigator.pop(context)` | [Dashboard, Add Student] → [Dashboard] | + +--- + +## 5. Demo: minimal Navigator on Week 2 app (copy-paste) + +Use this **before** introducing the full Student Management app. It uses the existing **week-2** project (Task Manager with `HomePage`). + +### Step A: Create a second screen + +Create file **`lib/second_screen.dart`** and paste: + +```dart +import 'package:flutter/material.dart'; + +class SecondScreen extends StatelessWidget { + const SecondScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Second Screen'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('You pushed to this screen.'), + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Go Back'), + ), + ], + ), + ), + ); + } +} +``` + +### Step B: Add a button on HomePage that pushes + +In **`lib/home_page.dart`**: + +1. Add the import at the top: + +```dart +import 'second_screen.dart'; +``` + +2. Add a button inside the `Column` (e.g. after the existing `Text` widgets, before the closing `],` of `children`): + +```dart +const SizedBox(height: 24), +ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SecondScreen(), + ), + ); + }, + child: const Text('Go to Second Screen'), +), +``` + +Run the app: tap "Go to Second Screen" → new screen appears (push). Tap "Go Back" → returns to HomePage (pop). + +--- + +## 6. Wiring in main.dart: initial screen and (optional) named routes + +The **main.dart** file configures the app’s **first screen** and, if you use them, **named routes**. Both live on **MaterialApp**. + +### 6.1 First screen only: use `home` + +For a single starting screen (e.g. Week 2 Task Manager), set **`home`**: + +```dart +MaterialApp( + title: 'Task Manager', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + useMaterial3: true, + ), + home: const HomePage(), // First screen the user sees +); +``` + +- **`home`** – The widget shown when the app starts (bottom of the stack). +- You still use **Navigator.push** / **Navigator.pop** from that screen to go to others. + +### 6.2 Named routes: use `initialRoute` and `routes` + +When you have several screens and want to refer to them by **name** (e.g. `/`, `/add`, `/students`), use **`initialRoute`** and **`routes`** instead of **`home`**: + +```dart +MaterialApp( + title: 'Student Management System', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + useMaterial3: true, + ), + initialRoute: '/', // Which route is the first screen + routes: { + '/': (context) => const StudentDashboardScreen(), + '/students': (context) => const StudentListScreen(), + '/add': (context) => const AddStudentScreen(), + }, +); +``` + +- **`initialRoute`** – The **name** of the first route (e.g. `'/'` = dashboard). +- **`routes`** – Map of **route name → screen**. When you push a named route, Flutter looks up the name here and builds that screen. + +**Important:** If you use **`routes`**, do **not** set **`home`**. Use **either** `home` **or** `initialRoute` + `routes`, not both. + +### 6.3 Going to a screen by name: pushNamed + +With named routes you can push by **name** instead of building the widget yourself: + +```dart +// Instead of: +Navigator.push( + context, + MaterialPageRoute(builder: (context) => const AddStudentScreen()), +); + +// You can write: +Navigator.pushNamed(context, '/add'); +``` + +**Pop** is the same: **`Navigator.pop(context)`** (no name needed). + +### 6.4 Summary + +| Approach | Use when | In main.dart | +|----------------|-----------------------------------|---------------------------------------| +| **`home`** | One starting screen, push others | `home: const HomePage()` | +| **Named routes** | Multiple “entry” screens by name | `initialRoute: '/'` and `routes: { ... }` | + +For the **minimal demo** (Section 5), **`home: const HomePage()`** is enough. For the **full Student Management app**, you can switch to **`initialRoute`** and **`routes`** and use **pushNamed** when you wire the dashboard and Add Student screen. + +--- + +## 7. After this: Add Student UI + +Once students understand push and pop: + +- **Dashboard** will **push** to **Add Student** when they tap "Add Student". +- **Add Student** screen will have a form (name, ID, course) and a button that **pops** to go back (and later can also save the student). + +The same Navigator concepts apply; the Add Student screen is just a more useful "second screen" with a form. + +See **LESSON_PLAN.md** for the full lesson plan and **STUDENT_DEVELOPMENT_GUIDE.md** (Section 7) for navigation in the Student Management app. diff --git a/week-guides/week2-guideline/SCAFFOLD_AND_WIDGETS_NOTES.md b/week-guides/week2-guideline/SCAFFOLD_AND_WIDGETS_NOTES.md new file mode 100644 index 0000000..5402707 --- /dev/null +++ b/week-guides/week2-guideline/SCAFFOLD_AND_WIDGETS_NOTES.md @@ -0,0 +1,151 @@ +# Scaffold and Widget Layout – Quick Reference + +Use this note when introducing **Scaffold**, **parameters**, and the **child vs children** pattern. Helps avoid mistakes like putting `children` on Scaffold or using `margin` as a function. + +--- + +## 1. What is Scaffold? + +**Scaffold** is the basic layout structure for a full screen. It gives you places to put an app bar, the main content, buttons, and drawers. + +--- + +## 2. What can go inside Scaffold? (Parameters) + +Scaffold has **named parameters**. You only use the ones you need. + +| Parameter | Type | What it is | +|-----------|------|------------| +| **appBar** | `AppBar?` | Bar at the top (title, actions, back button). Optional. | +| **body** | `Widget` | The main content of the screen. **Required** if you don’t use a different structure. Usually **one** widget (e.g. `Center`, `Column`, `ListView`). | +| **floatingActionButton** | `Widget?` | A floating button (e.g. “+” to add a task). Optional. | +| **bottomNavigationBar** | `Widget?` | Bar at the bottom (tabs, navigation). Optional. | +| **drawer** | `Widget?` | Slide-out menu from the left. Optional. | +| **backgroundColor** | `Color?` | Background color of the scaffold. Optional. | + +**Example:** + +```dart +Scaffold( + appBar: AppBar( + title: const Text('Task List'), + ), + body: Center( + child: const Text('Hello'), + ), + floatingActionButton: FloatingActionButton( + onPressed: () {}, + child: const Icon(Icons.add), + ), +) +``` + +**Important:** **`body`** takes **exactly one** widget. It does **not** have a **`children`** parameter. To show multiple widgets, put them inside a **Column** or **ListView** and pass that as `body`. + +--- + +## 3. Child vs children – the pattern + +Many layout widgets follow one of two patterns: + +### 3.1 Widgets with **one** child: `child:` + +These wrap **a single** widget. Use **`child:`** (singular). + +| Widget | Parameter | Use | +|--------|-----------|-----| +| **Padding** | `padding`, **`child`** | Space around one widget. | +| **Center** | **`child`** | Center one widget. | +| **Container** | `margin`, `padding`, **`child`** | Decoration, size, margin around one widget. | +| **Scaffold** | **`body`** (one widget) | The main area of the screen. | +| **ElevatedButton** | `onPressed`, **`child`** | Button with one label/icon. | + +**Example:** + +```dart +Center( + child: Padding( + padding: const EdgeInsets.all(16), + child: Container( + margin: const EdgeInsets.all(8), + child: const Text('Hello'), + ), + ), +) +``` + +### 3.2 Widgets with **multiple** children: `children:` + +These arrange **a list** of widgets. Use **`children:`** (plural) and a **list** `[ ... ]`. + +| Widget | Parameter | Use | +|--------|-----------|-----| +| **Column** | **`children`** | Stack widgets **vertically**. | +| **Row** | **`children`** | Place widgets **horizontally**. | +| **ListView** | **`children`** | Scrollable list of widgets. | +| **Stack** | **`children`** | Overlay widgets on top of each other. | + +**Example:** + +```dart +Column( + children: [ + const Text('First'), + ElevatedButton(onPressed: () {}, child: const Text('Click')), + const Text('Third'), + ], +) +``` + +--- + +## 4. Combining: body has one widget, that widget can have children + +**Scaffold** has **`body:`** (one widget). That one widget is often a **Column** or **ListView**, which **have** **`children:`**. + +``` +Scaffold + body: Column ← one widget for body + children: [ ← list of widgets inside Column + Text(...), + ElevatedButton(...), + ], +``` + +**Correct:** + +```dart +Scaffold( + body: Column( + children: [ + const Text('Task List Page'), + ElevatedButton( + onPressed: () => Navigator.pop(context), + child: const Text('Go Back'), + ), + ], + ), +) +``` + +**Wrong:** + +```dart +Scaffold( + body: children: [ ... ] // ❌ Scaffold has no "children". Use body: Column(children: [ ... ]) +) +``` + +--- + +## 5. Quick rules + +1. **Scaffold** → use **`body:`** with **one** widget (e.g. `Center`, `Column`, `ListView`). +2. **Single widget to wrap?** → use **`child:`** (Padding, Center, Container, etc.). +3. **Several widgets in a row or column?** → use **`children:`** inside **Column**, **Row**, or **ListView**. +4. **Margin** → use **`Container(margin: EdgeInsets.xxx, child: ...)`**, not a function called `margin`. +5. **Padding** → use **`Padding(padding: EdgeInsets.xxx, child: ...)`**. + +--- + +See **STUDENT_DEVELOPMENT_GUIDE.md** (Section 2) for a full screen example and **NAVIGATOR_NOTES.md** for navigation. diff --git a/week-guides/week2-guideline/STUDENT_DEVELOPMENT_GUIDE.md b/week-guides/week2-guideline/STUDENT_DEVELOPMENT_GUIDE.md new file mode 100644 index 0000000..eaf1ce5 --- /dev/null +++ b/week-guides/week2-guideline/STUDENT_DEVELOPMENT_GUIDE.md @@ -0,0 +1,245 @@ +# Student Development Guide + +**Mobile Development Industry Course** + +This guide helps students implement features for the **Student Management System App**. +If you get stuck, follow the guidelines below. + +**Session order:** Do **Introduction to Dart first** (**INTRODUCTION_TO_DART.md**), then **Navigator** (**NAVIGATOR_NOTES.md** and **demo-snippets/navigator-demo/**), then use this guide for screens, form, and navigation. See **LESSON_PLAN.md** for the full lesson plan. + +--- + +## 1. Basic Flutter Project Structure + +Inside your project you will typically see: + +``` +lib/ +├── main.dart +├── screens/ +├── widgets/ +└── models/ +``` + +**Explanation:** + +- **screens** → pages of the application +- **widgets** → reusable UI components +- **models** → data structures + +**Example:** `lib/screens/student_list_screen.dart` + +--- + +## 2. Creating a Simple UI Screen + +**See also:** **SCAFFOLD_AND_WIDGETS_NOTES.md** – what parameters Scaffold has (appBar, body, etc.) and the **child vs children** pattern (when to use `child:` and when to use `children:`). + +Example structure for a page: + +``` +Scaffold +├── AppBar +└── Body +``` + +**Example Flutter code:** + +```dart +import 'package:flutter/material.dart'; + +class StudentListScreen extends StatelessWidget { + const StudentListScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Students"), + ), + body: const Center( + child: Text("Student List Screen"), + ), + ); + } +} +``` + +--- + +## 3. Creating a ListView + +A **ListView** is used to display a list of items. + +**Example:** + +```dart +ListView.builder( + itemCount: students.length, + itemBuilder: (context, index) { + return ListTile( + title: Text(students[index].name), + subtitle: Text(students[index].course), + ); + }, +) +``` + +This will generate a scrollable list. + +--- + +## 4. Example Student Model + +**Example data model:** + +```dart +class Student { + final String name; + final String id; + final String course; + + Student({ + required this.name, + required this.id, + required this.course, + }); +} +``` + +**Example data:** + +```dart +final students = [ + Student(name: "John Doe", id: "S001", course: "CS"), + Student(name: "Jane Smith", id: "S002", course: "IT"), +]; +``` + +--- + +## 5. Creating a Form (Add Student) + +**Example input fields:** + +```dart +TextField( + decoration: InputDecoration( + labelText: "Student Name", + ), +) +``` + +**Example layout:** + +```dart +Column( + children: [ + TextField(...), + TextField(...), + ElevatedButton( + onPressed: () {}, + child: const Text("Add Student"), + ), + ], +) +``` + +--- + +## 6. Mock UI Example – Student Dashboard Screen + +``` +┌─────────────────────────────────────┐ +│ Student Management System │ +├─────────────────────────────────────┤ +│ │ +│ [ Add Student ] │ +│ │ +│ (List of students or empty state) │ +│ │ +└─────────────────────────────────────┘ +``` + +Students should aim to build something similar to this layout. + +--- + +## 7. Navigation Between Screens + +**Example navigation:** + +```dart +Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const StudentListScreen(), + ), +); +``` + +This will move to another page. + +--- + +## 8. Git Workflow (Important) + +Each group must work in a **feature branch**. + +**Example:** + +```bash +git checkout -b feature/student-list +``` + +**Commit changes:** + +```bash +git add . +git commit -m "feat: implement student list UI" +``` + +**Push to GitHub:** + +```bash +git push origin feature/student-list +``` + +Then create a **Pull Request** to merge into `main` (or `week-2`). + +--- + +## 9. What To Do If You Get Stuck + +Try the following: + +1. Check the **Flutter documentation** +2. Ask your **teammates** +3. **Search** the error message +4. Ask the **lecturer** for guidance + +**Remember:** Learning to solve problems is part of becoming a developer. + +--- + +## 10. Final Goal + +By completing this project you will learn: + +- Flutter UI development +- Git workflow +- Branching strategy +- Pull Requests +- Continuous Integration with GitHub Actions + +Your final output should be a **working Student Management App**. + +--- + +## How This Helps You During the Lecture + +If a group struggles you can simply say: + +> **“Check the Student Development Guide section for ListView.”** + +This avoids stopping the entire class. diff --git a/week-guides/week2-guideline/demo-snippets/add-student-ui/README.md b/week-guides/week2-guideline/demo-snippets/add-student-ui/README.md new file mode 100644 index 0000000..1ad0b18 --- /dev/null +++ b/week-guides/week2-guideline/demo-snippets/add-student-ui/README.md @@ -0,0 +1,20 @@ +# Add Student UI – Demo copy-paste + +Use during **Phase 2** of the teaching path (after Navigator). + +- **`add_student_screen.dart`** – Full screen: Scaffold, AppBar, form (name, ID, course), button that calls `Navigator.pop(context)`. + +**Where to put it:** `task_manager_app/lib/screens/add_student_screen.dart` (create `lib/screens/` if needed). + +**Then:** From HomePage (or your dashboard), add a button that pushes to this screen: + +```dart +Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AddStudentScreen(), + ), +); +``` + +Don’t forget to import: `import 'screens/add_student_screen.dart';` diff --git a/week-guides/week2-guideline/demo-snippets/add-student-ui/add_student_screen.dart b/week-guides/week2-guideline/demo-snippets/add-student-ui/add_student_screen.dart new file mode 100644 index 0000000..5c16883 --- /dev/null +++ b/week-guides/week2-guideline/demo-snippets/add-student-ui/add_student_screen.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +class AddStudentScreen extends StatelessWidget { + const AddStudentScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Add Student'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + TextField( + decoration: const InputDecoration( + labelText: 'Student Name', + ), + ), + const SizedBox(height: 16), + TextField( + decoration: const InputDecoration( + labelText: 'Student ID', + ), + ), + const SizedBox(height: 16), + TextField( + decoration: const InputDecoration( + labelText: 'Course', + ), + ), + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Add Student'), + ), + ], + ), + ), + ); + } +} diff --git a/week-guides/week2-guideline/demo-snippets/navigator-demo/README.md b/week-guides/week2-guideline/demo-snippets/navigator-demo/README.md new file mode 100644 index 0000000..7c21c08 --- /dev/null +++ b/week-guides/week2-guideline/demo-snippets/navigator-demo/README.md @@ -0,0 +1,10 @@ +# Navigator demo – Copy-paste for Week 2 app + +Use during **Phase 1** of the teaching path (Navigator first). + +1. **Create** `task_manager_app/lib/second_screen.dart` and paste the contents of **`second_screen.dart`** from this folder. +2. **Edit** `task_manager_app/lib/home_page.dart`: + - Add import: `import 'second_screen.dart';` + - Add the button snippet from **`home_page_add_button.txt`** inside the `Column`’s `children`. + +Run the app: tap "Go to Second Screen" (push), then "Go Back" (pop). diff --git a/week-guides/week2-guideline/demo-snippets/navigator-demo/home_page_add_button.txt b/week-guides/week2-guideline/demo-snippets/navigator-demo/home_page_add_button.txt new file mode 100644 index 0000000..b78ce19 --- /dev/null +++ b/week-guides/week2-guideline/demo-snippets/navigator-demo/home_page_add_button.txt @@ -0,0 +1,16 @@ +=== ADD TO TOP OF lib/home_page.dart (imports) === +import 'second_screen.dart'; + +=== ADD INSIDE Column children, AFTER the last Text(...) and BEFORE the closing ], === + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SecondScreen(), + ), + ); + }, + child: const Text('Go to Second Screen'), + ), diff --git a/week-guides/week2-guideline/demo-snippets/navigator-demo/second_screen.dart b/week-guides/week2-guideline/demo-snippets/navigator-demo/second_screen.dart new file mode 100644 index 0000000..9ea59b3 --- /dev/null +++ b/week-guides/week2-guideline/demo-snippets/navigator-demo/second_screen.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class SecondScreen extends StatelessWidget { + const SecondScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Second Screen'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('You pushed to this screen.'), + const SizedBox(height: 24), + ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Go Back'), + ), + ], + ), + ), + ); + } +} diff --git a/week-guides/week2.md b/week-guides/week2.md index a0c9651..fe0866d 100644 --- a/week-guides/week2.md +++ b/week-guides/week2.md @@ -1,53 +1,79 @@ -# Week 2 – Flutter UI Fundamentals +# Week 2 – Git, GitHub, CI & Flutter basics **Branch:** `week-2` ## Learning Objectives -- Widget tree and composition -- StatelessWidget vs StatefulWidget -- Layout: Row, Column, Expanded, Stack -- Reusable UI components -- Forms and validation +- **Git workflow** – branching, commits, pull requests +- **GitHub repositories** – cloning, collaboration +- **GitHub Actions (CI)** – automated checks on push/PR +- **Basic Flutter development** – run the app and understand structure +- **Writing simple tests** – widget tests with `testWidgets`, `find`, `expect` +- **Professional repository structure** – `.github/workflows` and documentation ## Teaching Flow -1. **Whiteboard:** Widget tree; Stateless vs Stateful -2. **Live coding:** Build: - - Login screen (email + password, basic validation) - - Task list screen (list of tasks) - - Task card widget (reusable) -3. Use: `Column`, `Row`, `Expanded`, `ListView`, `Form`, `TextFormField` +1. **Git & GitHub:** Clone repo, create a feature branch, make a small change, commit, push, open a PR. +2. **CI demo:** Show the GitHub **Actions** tab and how CI runs on the PR. +3. **Repo tour:** `README.md`, `CONTRIBUTING.md`, `CI_EXPLANATION.md`, `.github/workflows/flutter-ci.yml`. +4. **Introduction to Dart (first):** Use **week2-guideline/INTRODUCTION_TO_DART.md** – variables, types, functions, classes, `const`, enough to read Flutter code. +5. **Flutter basics:** Run the app; explain `pubspec.yaml`, `lib/main.dart`, and `home_page.dart`. +6. **Navigator:** Use **week2-guideline/NAVIGATOR_NOTES.md** and **demo-snippets/navigator-demo/** to explain push/pop and do a minimal second-screen demo. +7. **Add Student UI:** Build the form screen (Scaffold, TextFields, button with `Navigator.pop`); use **demo-snippets/add-student-ui/** or **STUDENT_DEVELOPMENT_GUIDE.md**. +8. **Testing basics:** Open `test/widget_test.dart`; explain `pumpWidget`, `find`, `expect`; run `flutter test`. ## Repo Structure (Week 2) ``` -lib/ -├── main.dart -├── screens/ -│ ├── login_screen.dart -│ └── task_list_screen.dart -├── widgets/ -│ └── task_card.dart -└── home_page.dart (or navigation from main) +.github/workflows/ + flutter-ci.yml +task_manager_app/ + lib/ + test/ +README.md +CONTRIBUTING.md +CI_EXPLANATION.md ``` -## Key Concepts - -- **StatelessWidget** – no mutable state -- **StatefulWidget** – state that can change; `setState()` -- **Layout:** Row, Column, Expanded, Stack -- **Form** – `GlobalKey`, validator callbacks - ## Student Exercises -- Add a “Forgot password?” link on the login screen -- Style the task card (e.g. color by completion status) -- Add a simple form to “Create task” (title only) +- Create a branch, change a text in the app, open a PR, and confirm CI runs and passes. +- Add one more `expect(...)` assertion to `test/widget_test.dart` and run `flutter test`. +- Read `CI_EXPLANATION.md` and summarize CI in 3–5 sentences. ## Check Out This Week ```bash git checkout week-2 -cd task_manager_app && flutter pub get && flutter run +cd task_manager_app +flutter pub get +flutter run +flutter test ``` + +--- + +## 1.5‑hour session (8 groups) + +For the hands-on part of the session, use the **week2-group-activity** folder: + +- **Instructor:** **week2-group-activity/week2-session-guide.md** – timing, group–task mapping, tips. +- **Groups 1–8:** **week2-group-activity/week2-group-1.md** … **week2-group-8.md** – one task per group (branch, change, commit, push, PR, CI). + +--- + +## Student guidelines and reference solution + +In **week2-guideline** you will find: + +- **week2-guideline/LESSON_PLAN.md** – **Lesson plan** for the session: Dart intro first, then Navigator, then Add Student UI. Timings and pointers to all notes and demo files (good to follow when sharing the repo on screen). +- **week2-guideline/INTRODUCTION_TO_DART.md** – **Introduction to Dart** (do first): variables, types, functions, classes, `const`, List – enough to read and write simple Flutter code. +- **week2-guideline/NAVIGATOR_NOTES.md** – Navigator concepts (stack, push, pop) and a minimal copy-paste demo (second screen + button on HomePage). Use **before** Add Student UI. +- **week2-guideline/SCAFFOLD_AND_WIDGETS_NOTES.md** – What goes inside Scaffold (appBar, body, etc.), **child** vs **children**, and layout patterns (Padding, Container, Column, Row). +- **week2-guideline/demo-snippets/** – Copy-paste files for live demo: + - **navigator-demo/** – `second_screen.dart` and snippet to add a "Go to Second Screen" button on HomePage. + - **add-student-ui/** – Full `add_student_screen.dart` (form + button that pops). +- **week2-guideline/STUDENT_DEVELOPMENT_GUIDE.md** – Project structure, screens, ListView, model, form, navigation, Git workflow, and what to do if stuck. +- **week2-guideline/GROUP_TASKS.md** – Pointer to group activity task files. +- **week2-guideline/CI_EXPLANATION.md** – Short CI pointer (full docs in repo root). +- **week2-guideline/complete-solution/** – Reference solution (Student Management App) with copy-paste instructions and files. **This folder is on the `week2-solution` branch only.** See **complete-solution/README.md** for paste order.