Dart dla początkujących
Przykłady i zadania z warsztatu done by Szymon.
Lekcja 1 - Witaj Świecie
print("Witaj świecie!");
Lekcja 2 - Zmienne
var zmienna = "Witaj świecie!";print(zmienna);
Lekcja 3 - Zmiana wartości zmiennej
var zmienna2 = "Witaj świecie!";zmienna2 = "Cześć Solvro!!!";print(zmienna2);
Lekcja 4 - Jawna deklaracja typu przy zmiennej
String zmienna3 = "Cześć Solvro!!!";print(zmienna3);
Lekcja 5 - Podstawowe typy
int zmiennaInt = 666; // liczby całkowitedouble zmiennaDouble = 4.5673547682345782; // liczby z przecinkiemnum zmiennaNum = 55.4; // int LUB doubleString zmiennaString = "Ciąg znaków! Mogą tez być nawet emoji 😱😱😱";bool zmiennaBoolean = true; // true or false
Lekcja 6 - var vs final
var changeable = "Wartość początkowa";changeable = "Nowa wartość";print(changeable);
final unchangeable = "Tego nie można zmienić";// unchangeable = "Próba zmiany"; // To spowoduje errorprint(unchangeable);
Lekcja 7 - final vs const
final now = DateTime.now(); // Wartość jest określana w czasie działania programu, ale nie mozna jej potem zmieniać.print(now);
const toJestConst = "To jest stała czasu kompilacji"; // Wartość musi być znana podczas czasu kompilacjiprint(toJestConst);
// pokazać potem na przykładzie listy ze final nie mozna zmieniac przypisania, ale mozna mutować jesli cos jest mutable
Lekcja 8 - Standardowe wejście (stdin) - nie wspierane przez dartpad.dev
import 'dart:io';
void main() async { // Podstawowe wczytywanie tekstu stdout.write('Podaj swoje imię: '); String? name = stdin.readLineSync(); print('Witaj, $name!');}
Lekcja 9 - Operatory arytmetyczne
void main() { // Dodawanie int suma = 5 + 3; // 8 print("Suma: $suma");
// Odejmowanie int roznica = 10 - 4; // 6 print("Różnica: $roznica");
// Mnożenie int iloczyn = 6 * 7; // 42 print("Iloczyn: $iloczyn");
// Dzielenie (zwraca double) double iloraz = 15 / 3; // 5.0 print("Iloraz: $iloraz");
// Dzielenie całkowite (zwraca int) int dzielenieCalkowite = 15 ~/ 3; // 5 print("Dzielenie całkowite: $dzielenieCalkowite");
// Reszta z dzielenia (modulo) int reszta = 17 % 5; // 2 print("Reszta z dzielenia: $reszta");
// Operatory inkrementacji i dekrementacji int liczba = 5; liczba++; // inkrementacja (liczba = liczba + 1) print("Po inkrementacji: $liczba"); // 6
liczba--; // dekrementacja (liczba = liczba - 1) print("Po dekrementacji: $liczba"); // 5
// Operatory skrócone int x = 5; x += 3; // x = x + 3 print("Po dodaniu 3: $x"); // 8
x -= 2; // x = x - 2 print("Po odjęciu 2: $x"); // 6
x *= 2; // x = x * 2 print("Po pomnożeniu przez 2: $x"); // 12
x ~/= 3; // x = x ~/ 3 (dzielenie całkowite) print("Po podzieleniu przez 3: $x"); // 4}
Lekcja 10 - pierwastkowanie i potęgowanie z dart:math
import 'dart:math';
void main() { // Potęgowanie final potega = pow(2, 3); // 8 print("Potęga: $potega");
// Pierwiastek kwadratowy final pierwiastek = sqrt(16); // 4.0 print("Pierwiastek: $pierwiastek");}
Lekcja 11 - Operacje na stringach
const tekst = " Witaj w Solvro! ";
// Długość stringaprint(tekst.length); // 19
// Konwersja na wielkie literyprint(tekst.toUpperCase()); // " WITAJ W SOLVRO! "
// Konwersja na małe literyprint(tekst.toLowerCase()); // " witaj w solvro! "
// Usuwanie białych znaków z początku i końcaprint(tekst.trim()); // "Witaj w Solvro!"
// Sprawdzanie czy string zawiera podciągprint(tekst.contains("Solvro")); // trueprint(tekst.contains("Flutter")); // false
// Zamiana podciąguprint(tekst.replaceAll("Solvro", "Flutter")); // " Witaj w Flutter! "
// Łączenie stringówconst imie = "Jan";const nazwisko = "Kowalski";final pelneImie = imie + " " + nazwisko;print(pelneImie); // "Jan Kowalski"
// Sprawdzanie czy string zaczyna się od danego podciąguprint(tekst.startsWith(" Witaj")); // true
// Sprawdzanie czy string kończy się danym podciąguprint(tekst.endsWith("! ")); // true
// Pobieranie podciąguprint(tekst.substring(2, 7)); // "Witaj"
// Powtarzanie stringaprint("Ha".padRight(5, "!")); // "Ha!!!"print("Ha".padLeft(5, "!")); // "!!!Ha"
// Sprawdzanie czy string jest pustyfinal pusty = "";print(pusty.isEmpty); // trueprint(pusty.isNotEmpty); // false
Lekcja 12 - Interpolacja stringów
const name = "Solvro";const number = 123;const pi = 3.14159;
print("Witaj, $name!");print("Numer: $number");print("Pi: $pi");print("Wynik dodawania 5 i 3 to ${5 + 3}"); // wyrażenie w stringuprint("Długość Twojego imienia to ${name.length}"); // właściwość w stringu
🎯 Zadanie 1 - Kalkulator prosty
Stwórz prosty kalkulator, który:
- Zdefiniuje dwie zmienne liczbowe (np.
a = 10
ib = 5
) - Wykona wszystkie podstawowe operacje matematyczne (dodawanie, odejmowanie, mnożenie, dzielenie)
- Wyświetli wyniki używając interpolacji stringów
- Dodatkowo obliczy i wyświetli potęgę
a
dob
oraz pierwiastek kwadratowy za
Przykład oczekiwanego wyniku:
a = 10, b = 5Dodawanie: 10 + 5 = 15Odejmowanie: 10 - 5 = 5Mnożenie: 10 * 5 = 50Dzielenie: 10 / 5 = 2.0Potęga: 10^5 = 100000Pierwiastek z 10 = 3.1622776601683795
Jeśli masz zainstalowane środowisko darta na swojej maszynie: Możesz pobierać wartości a i b z wejścia standardowego.
Lekcja 13 - Listy
List<String> names = ["Dawid", "Marek", "Linek"];// const names = ["Dawid", "Marek", "Linek"]; // również dozwolone, nawet zalecaneprint(names[0]); // Dostęp do elementównames.add("Szymon"); // Dodawanie elementunames[1] = "DaWiDD"; // Podmiana elementu na indeksieprint(names);
Lekcja 14 - Podstawowe operacje na listach
void main() { // Tworzenie listy List<String> owoce = ["jabłko", "gruszka"];
// Dodawanie elementów owoce.add("śliwka"); print(owoce); // [jabłko, gruszka, śliwka]
// Dodawanie wielu elementów owoce.addAll(["banan", "pomarańcza"]); print(owoce); // [jabłko, gruszka, śliwka, banan, pomarańcza]
// Wstawianie elementu na konkretną pozycję owoce.insert(1, "malina"); print(owoce); // [jabłko, malina, gruszka, śliwka, banan, pomarańcza]
// Usuwanie elementu owoce.remove("gruszka"); print(owoce); // [jabłko, malina, śliwka, banan, pomarańcza]
// Usuwanie elementu z konkretnej pozycji owoce.removeAt(0); print(owoce); // [malina, śliwka, banan, pomarańcza]
// Sprawdzanie czy lista zawiera element print(owoce.contains("banan")); // true
// Długość listy print(owoce.length); // 4
// Czyszczenie listy owoce.clear(); print(owoce); // []}
Lekcja 15 - final/const a mutowalność
void main() { // final z listą - można mutować zawartość, ale nie można przypisać nowej listy final List<String> finalList = ["jabłko", "gruszka"];
// To działa - mutujemy zawartość finalList.add("śliwka"); finalList[0] = "banan"; print(finalList); // [banan, gruszka, śliwka]
// To NIE zadziała - próba przypisania nowej listy // finalList = ["pomarańcza", "mandarynka"]; // Błąd kompilacji!
// const z listą - NIE można mutować zawartości const List<String> constList = ["jabłko", "gruszka"];
// To NIE zadziała - próba mutacji const listy // constList.add("śliwka"); // Błąd runtime! // constList[0] = "banan"; // Błąd runtime!
// To NIE zadziała - próba przypisania nowej listy // constList = ["pomarańcza", "mandarynka"]; // Błąd kompilacji!
}
Kluczowe różnice:
final
- nie można przypisać nowej wartości, ale można mutować zawartość (dla kolekcji i obiektów)const
- nie można przypisać nowej wartości ani mutować zawartości
Lekcja 16 - Konwersja Stringów i List
// Podział stringa na listęString listaZakupow = "jabłka, gruszki, śliwki";List<String> owoce = listaZakupow.split(", ");print(owoce); // ["jabłka", "gruszki", "śliwki"]
// Łączenie listy w stringList<String> zwierzeta = ["kot", "pies", "chomik"];String zwierzetaString = zwierzeta.join(", ");print(zwierzetaString); // "kot, pies, chomik"
// Można też użyć innego separatoraString zwierzetaKropka = zwierzeta.join(".");print(zwierzetaKropka); // "kot.pies.chomik"
Lekcja 17 - Mapy (Słowniki)
Map<String, int> ages = {"Dawid": 30, "Marek": 25, "Linek": 18};print(ages["Marek"]); // Dostęp do wartościages["Szymon"] = 22; // Dodawanie pary klucz-wartośćages["Linek"] = 5; // zmiana wartościprint(ages);
Lekcja 18 - Podstawowe operacje na mapach
void main() { // Tworzenie mapy Map<String, int> wiek = { "Jan": 25, "Anna": 30, };
// Dodawanie elementu wiek["Piotr"] = 28; print(wiek); // {Jan: 25, Anna: 30, Piotr: 28}
// Zmiana wartości wiek["Jan"] = 26; print(wiek); // {Jan: 26, Anna: 30, Piotr: 28}
// Usuwanie elementu wiek.remove("Anna"); print(wiek); // {Jan: 26, Piotr: 28}
// Sprawdzanie czy klucz istnieje print(wiek.containsKey("Jan")); // true print(wiek.containsKey("Anna")); // false
// Sprawdzanie czy wartość istnieje print(wiek.containsValue(28)); // true
// Pobieranie wszystkich kluczy print(wiek.keys); // (Jan, Piotr)
// Pobieranie wszystkich wartości print(wiek.values); // (26, 28)
// Czyszczenie mapy wiek.clear(); print(wiek); // {}}
Lekcja 19 - Zbiory
// Tworzenie zbioruSet<String> owoce = {"jabłko", "gruszka", "śliwka"};print(owoce); // {jabłko, gruszka, śliwka}
// Dodawanie elementówowoce.add("banan");print(owoce); // {jabłko, gruszka, śliwka, banan}
// Próba dodania duplikatu (nie zadziała)owoce.add("jabłko");print(owoce); // {jabłko, gruszka, śliwka, banan}
// Usuwanie elementówowoce.remove("gruszka");print(owoce); // {jabłko, śliwka, banan}
// Sprawdzanie czy element istniejeprint(owoce.contains("jabłko")); // trueprint(owoce.contains("gruszka")); // false
// Operacje na zbiorachSet<String> owoce2 = {"banan", "pomarańcza", "jabłko"};
// Suma zbiorówSet<String> wszystkieOwoce = owoce.union(owoce2);print(wszystkieOwoce); // {jabłko, śliwka, banan, pomarańcza}
// Część wspólnaSet<String> wspolneOwoce = owoce.intersection(owoce2);print(wspolneOwoce); // {jabłko, banan}
// Różnica zbiorówSet<String> tylkoWOwoce = owoce.difference(owoce2);print(tylkoWOwoce); // {śliwka}
// Konwersja listy na zbiór (usunięcie duplikatów)List<String> listaZDuplikatami = ["a", "b", "a", "c", "b"];Set<String> zbiorBezDuplikatow = listaZDuplikatami.toSet();print(zbiorBezDuplikatow); // {a, b, c}
🎯 Zadanie 2 – Zarządzanie kolekcjami
Stwórz program, który:
- Tworzy listę zakupów (np.
['chleb', 'mleko', 'jajka']
). - Dodaje do listy dwa nowe produkty, w tym jeden który już w nim był
- Zamienia jeden z produktów na inny.
- Usuwa jeden produkt z listy.
- Tworzy mapę, gdzie kluczem jest nazwa produktu, a wartością liczba sztuk (np.
{'chleb': 2, 'mleko': 1}
). - Zwiększa liczbę sztuk jednego z produktów.
- Tworzy zbiór wszystkich unikalnych produktów z listy zakupów (usuwa duplikaty z listy).
- Wypisuje na końcu:
- aktualną listę zakupów,
- mapę produktów,
- zbiór unikalnych produktów.
- Wypisz listę zakupów, oddzielając każdy element wybraną emotką
Przykład oczekiwanego wyniku:
Lista zakupów: [chleb, jajka, masło, ser, chleb]Mapa produktów: {chleb: 2, mleko: 1, masło: 1}Unikalne produkty: {chleb, jajka, masło, ser}Emoji-lista: chleb 🍷 jajka 🍷 masło 🍷 ser 🍷 chleb
Lekcja 20 - Null safety
String? nullableString; // Może być nullString nonNullableString = "Zainicjalizowano";
print(nullableString); // printuje null (jeśli nie przypisano wartości)print(nonNullableString);
nullableString = "juzNieNull, ale typ nadal pozwala na nulla";print(nullableString); // juz wyprintuje wartosc wyzej, ale sprawdzacz typow nadal będzie kazał nam uwazać na nulla.
// spróbuj przypisać null do nienullable zmiennej// nonNullableString = null;
Lekcja 21 - Po co jest null safety?
Null safety w Darcie pilnuje nas, żebyśmy zawsze sprawdzali czy wartość nie jest null przed jej użyciem. Oto przykład:
void main() { // Bez null safety (stary sposób) - mogłoby spowodować błąd runtime String? name; // Może być null
// To spowodowałoby błąd runtime w starym Darcie: // print(name.length); // NullPointerException!
// Null safety wymusza na nas sprawdzenie: if (name != null) { print(name.length); // Teraz jest bezpieczne } else { print("Imię nie zostało podane"); }
// Inny przykład - praca z listą List<String>? names;
// Bez sprawdzenia mogłoby być niebezpieczne: // print(names[0]); // NullPointerException!
// Null safety wymusza sprawdzenie: if (names != null && names.isNotEmpty) { print("Pierwsze imię: ${names[0]}"); } else { print("Lista jest pusta lub null"); }}
Lekcja 22 - Null-aware operatory (skróty dla null safety)
Null-aware operatory to skróty, które zastępują długie sprawdzenia null. Oto jak wyglądają te same operacje z użyciem operatorów:
void main() { // Operator ?. (null-aware access) - skrót dla if (name != null) String? name; print(name?.length); // null - bezpieczny dostęp do właściwości name = "Jan"; print(name?.length); // 3
// To samo co: // if (name != null) { // print(name.length); // } else { // print(null); // }
// Operator ?? (null coalescing) - skrót dla if-else String? surname; String fullName = surname ?? "Nieznane"; // Jeśli surname jest null, użyj "Nieznane" print(fullName); // "Nieznane"
// To samo co: // String fullName; // if (surname != null) { // fullName = surname; // } else { // fullName = "Nieznane"; // }
// Operator ??= (null-aware assignment) - skrót dla if (city == null) String? city; city ??= "Warszawa"; // Przypisz wartość tylko jeśli city jest null print(city); // "Warszawa" city ??= "Kraków"; // Nie zmieni wartości, bo city już ma wartość print(city); // nadal "Warszawa"
// To samo co: // if (city == null) { // city = "Warszawa"; // }
// Operator !. (null assertion) - mówimy kompilatorowi "jestem pewien" String? text = "Hello"; print(text!.length); // 5 - mówimy kompilatorowi, że jesteśmy pewni, że text nie jest null // Uwaga: używaj !. tylko gdy jesteś pewien, że wartość nie jest null!
// To samo co: // if (text != null) { // print(text.length); // } else { // throw Exception("text nie może być null!"); // }
// Łączenie operatorów - skróty dla złożonych sprawdzeń String? firstName; String? lastName; String displayName = firstName ?? lastName ?? "Anonim"; print(displayName); // "Anonim"
// To samo co: // String displayName; // if (firstName != null) { // displayName = firstName; // } else if (lastName != null) { // displayName = lastName; // } else { // displayName = "Anonim"; // }
// Bezpieczne wywołanie metody String? message; message?.toUpperCase(); // null - bezpieczne wywołanie metody message = "hello"; print(message?.toUpperCase()); // "HELLO"
// To samo co: // if (message != null) { // print(message.toUpperCase()); // }
// Bezpieczny dostęp do indeksu List<String>? names; print(names?[0]); // null - bezpieczny dostęp do indeksu names = ["Jan", "Anna"]; print(names?[0]); // "Jan"
// To samo co: // if (names != null) { // print(names[0]); // }
// // at this point jeszcze nie było funckji, więc się nie przejmuj, ale mozesz potem tutaj wrócić: //
// Bezpieczne wywołanie funkcji void Function()? callback; callback?.call(); // null - bezpieczne wywołanie funkcji callback = () => print("Callback!"); callback?.call(); // "Callback!"
// To samo co: // if (callback != null) { // callback(); // }}
Lekcja 23 - Konwersja typów
void main() { // Konwersja między typami numerycznymi int liczbaCalkowita = 42; double liczbaZmiennoprzecinkowa = liczbaCalkowita.toDouble(); print("Int na double: $liczbaZmiennoprzecinkowa"); // 42.0
double pi = 3.14; int zaokraglonaPi = pi.toInt(); print("Double na int: $zaokraglonaPi"); // 3
// Konwersja na string String liczbaJakoString = liczbaCalkowita.toString(); print("Int na String: $liczbaJakoString"); // "42"
// Konwersja ze stringa String tekstLiczby = "123"; int liczbaZeStringa = int.parse(tekstLiczby); print("String na int: $liczbaZeStringa"); // 123
String tekstZmiennoprzecinkowej = "3.14"; double liczbaZmiennoprzecinkowaZeStringa = double.parse(tekstZmiennoprzecinkowej); print("String na double: $liczbaZmiennoprzecinkowaZeStringa"); // 3.14
// Bezpieczna konwersja ze stringa String niepoprawnyTekst = "abc"; int? bezpiecznaLiczba = int.tryParse(niepoprawnyTekst); print("Bezpieczna konwersja: $bezpiecznaLiczba"); // null
// Bezpieczna konwersja double ze stringa String niepoprawnyTekstDouble = "xyz"; double? bezpiecznaLiczbaDouble = double.tryParse(niepoprawnyTekstDouble); print("Bezpieczna konwersja double: $bezpiecznaLiczbaDouble"); // null
String poprawnyTekstDouble = "2.71"; double? poprawnaLiczbaDouble = double.tryParse(poprawnyTekstDouble); print("Bezpieczna konwersja double: $poprawnaLiczbaDouble"); // 2.71
// at this point jeszcze nie wiedzie czym są operatory typu map itp, więc nie przejmujcie czym to jest ale mozecie potem do tego wrócić
// Konwersja między typami kolekcji List<int> liczby = [1, 2, 3]; List<String> liczbyJakoStringi = liczby.map((n) => n.toString()).toList(); print("Lista int na listę String: $liczbyJakoStringi"); // ["1", "2", "3"]
// Konwersja między typami map Map<String, int> mapa = {"jeden": 1, "dwa": 2}; Map<String, String> mapaJakoStringi = mapa.map( (key, value) => MapEntry(key, value.toString()) ); print("Mapa int na mapę String: $mapaJakoStringi"); // {"jeden": "1", "dwa": "2"}
// Konwersja między typami zbiorów Set<int> zbiorLiczb = {1, 2, 3}; Set<String> zbiorStringow = zbiorLiczb.map((n) => n.toString()).toSet(); print("Zbiór int na zbiór String: $zbiorStringow"); // {"1", "2", "3"}}
Lekcja 24 - Operatory porównania
void main() { int a = 5; int b = 10;
// Podstawowe operatory porównania print(a == b); // false - czy równe print(a != b); // true - czy różne print(a < b); // true - czy mniejsze print(a > b); // false - czy większe print(a <= b); // true - czy mniejsze lub równe print(a >= b); // false - czy większe lub równe
// Porównania z różnymi typami String tekst1 = "kot"; String tekst2 = "pies"; print(tekst1 == tekst2); // false
// Porównania z wartościami null int? liczba = null; print(liczba == null); // true}
Lekcja 25 - Operatory logiczne
void main() { bool a = true; bool b = false;
// Podstawowe operatory logiczne print(a && b); // false - AND (i) print(a || b); // true - OR (lub) print(!a); // false - NOT (nie)
// Przykład praktyczny int wiek = 20; bool maKartę = true;
if (wiek >= 18 && maKartę) { print("Możesz wejść do klubu"); }
// Łączenie operatorów bool jestSłonecznie = true; bool jestCiepło = true; bool jestWeekend = false;
if (jestSłonecznie && jestCiepło || jestWeekend) { print("Dobry dzień na spacer!"); }}
Lekcja 26 - Instrukcje warunkowe
int age = 20;if (age >= 18) { print("Dorosły");} else { print("Nieletni");}
Lekcja 27 - Operator trójargumentowy
Jest to skrócona wersja ifa.
String status = age >= 18 ? "Dorosły" : "Nieletni";print(status);
// to samo co:// String status;// if (age >= 18) {// status = "Dorosły";// } else {// status = "Nieletni";// }// print(status);
Lekcja 28 - Pętla for
// Iteracja po liścieList<String> owoce = ["jabłko", "gruszka", "śliwka"];for (final owoc in owoce) { print("Mam $owoc");}
// Podstawowa pętla for (wykona się 5 razy)for (var i = 0; i < 5; i++) { print("Iteracja $i");}
Lekcja 29 - Pętla while
// Podstawowa pętla whileint licznik = 0;while (licznik < 5) { print("Licznik: $licznik"); licznik++;}
// Pętla do-while (wykona się przynajmniej raz)int x = 0;do { print("Wartość x: $x"); x++;} while (x < 3);
Lekcja 30 - Break i continue
// Użycie break do przerwania pętlifor (int i = 0; i < 10; i++) { if (i == 5) { break; // przerywa pętlę gdy i osiągnie 5 } print("i = $i");}
// Użycie continue do pominięcia iteracjifor (int i = 0; i < 5; i++) { if (i == 2) { continue; // pomija iterację gdy i jest równe 2 } print("i = $i");}
// Przykład z while i breakint liczba = 0;while (true) { if (liczba >= 5) { break; } print("Liczba: $liczba"); liczba++;}
Lekcja 31 - Kombinacje pętli for i instrukcji if
void main() { // Przykład 1: Znajdowanie liczb parzystych w zakresie print("Liczby parzyste od 1 do 10:"); for (int i = 1; i <= 10; i++) { if (i % 2 == 0) { print("$i jest parzyste"); } }
// Przykład 2: Liczenie liczb większych od średniej List<int> liczby = [5, 12, 3, 8, 15, 7]; int suma = 0;
// Obliczanie średniej for (int liczba in liczby) { suma += liczba; } double srednia = suma / liczby.length;
// Liczenie liczb większych od średniej int licznik = 0; for (int liczba in liczby) { if (liczba > srednia) { licznik++; } } print("Średnia: $srednia"); print("Liczb większych od średniej: $licznik");
// Przykład 3: Znajdowanie pierwszego elementu spełniającego warunek List<String> imiona = ["Anna", "Jan", "Piotr", "Maria", "Kasia"]; String? znalezioneImie;
for (String imie in imiona) { if (imie.length > 4) { znalezioneImie = imie; break; // Znaleźliśmy pierwsze imię dłuższe niż 4 znaki } }
if (znalezioneImie != null) { print("Pierwsze imię dłuższe niż 4 znaki: $znalezioneImie"); } else { print("Nie znaleziono imienia dłuższego niż 4 znaki"); }
// Przykład 4: Filtrowanie i transformacja danych List<int> oceny = [2, 5, 4, 3, 5, 1, 4, 5]; List<String> wyniki = [];
for (int ocena in oceny) { if (ocena >= 4) { wyniki.add("Dobra ocena: $ocena"); } else if (ocena >= 2) { wyniki.add("Przeciętna ocena: $ocena"); } else { wyniki.add("Słaba ocena: $ocena"); } }
print("Wyniki ocen:"); for (String wynik in wyniki) { print(wynik); }
// Przykład 5: Znajdowanie maksimum i minimum List<int> temperatury = [15, 22, 8, 30, 12, 25, 18]; int? maxTemp; int? minTemp;
for (int temp in temperatury) { if (maxTemp == null || temp > maxTemp) { maxTemp = temp; } if (minTemp == null || temp < minTemp) { minTemp = temp; } }
print("Maksymalna temperatura: $maxTemp°C"); print("Minimalna temperatura: $minTemp°C");
// Przykład 6: Sprawdzanie czy wszystkie elementy spełniają warunek List<int> wieki = [18, 25, 22, 19, 21]; bool wszystkiePelnoletnie = true;
for (int wiek in wieki) { if (wiek < 18) { wszystkiePelnoletnie = false; break; // Nie musimy sprawdzać dalej } }
if (wszystkiePelnoletnie) { print("Wszystkie osoby są pełnoletnie"); } else { print("Nie wszystkie osoby są pełnoletnie"); }
// Przykład 7: Grupowanie elementów List<String> zwierzeta = ["kot", "pies", "krowa", "kura", "koń", "kaczka"]; List<String> zwierzetaNaK = []; List<String> inneZwierzeta = [];
for (String zwierze in zwierzeta) { if (zwierze.startsWith("k")) { zwierzetaNaK.add(zwierze); } else { inneZwierzeta.add(zwierze); } }
print("Zwierzęta na 'k': $zwierzetaNaK"); print("Inne zwierzęta: $inneZwierzeta");
// Przykład 8: Bezpieczne parsowanie stringów na liczby List<String> daneWejsciowe = ["123", "456", "abc", "789", "xyz", "42", "3.14"]; List<int> poprawneLiczby = []; List<String> niepoprawneDane = [];
for (String dane in daneWejsciowe) { int? sparsowanaLiczba = int.tryParse(dane); if (sparsowanaLiczba != null) { poprawneLiczby.add(sparsowanaLiczba); } else { niepoprawneDane.add(dane); } }
print("Poprawnie sparsowane liczby: $poprawneLiczby"); print("Niepoprawne dane: $niepoprawneDane"); print("Suma wszystkich poprawnych liczb: ${poprawneLiczby.reduce((a, b) => a + b)}");}
🎯 Zadanie 3 - Gra w zgadywanie
Stwórz prostą grę w zgadywanie liczby, która:
- Generuje losową liczbę od 1 do 100. Celem zadania jest symulacja zgadywania tej liczby przez uzytkownika. Wylosuj ją raz dla całego zadania (przed pętlą)
- Symuluje próby użytkownika. Użyj predefiniowanej listy prób. Załóż, że użytkownik podaje ci stringi i czasami może się pomylić i dać niepoprawną liczbę. Przykładowe wejście do zadania:
const strzaly = ["1","2","77","88","Kocham Solvro", "111.3287964", "23.32", "23.", ".43", "...34", r"$%^", "91","32","18"]
- Sprawdza czy podana liczba jest: (tip: uzywając pętli do iteracji po próbach)
- za mała (wypisuje “Za mało!”)
- za duża (wypisuje “Za dużo!”)
- poprawna (wypisuje “Gratulacje! Zgadłeś w X próbach!”)
- jezeli próba nie jest liczbą to ją pomija albo wypisuje “to nie liczba durniu!”
- Przerywa pętlę jesli uzytkownik zgadnie.
- Liczy liczbę prób.
- Używaj pętli do iteracji po próbach. Pamiętaj ze kazdą próbę (podaną na wejsciu w postaci stringa) trzeba sparsować (bezpiecznie) ma liczbę.
Wskazówki:
- Użyj
import 'dart:math';
iRandom().nextInt(100) + 1
do generowania losowej liczby.
Przykład oczekiwanego wyniku:
Próba 1: 50Za dużo!Próba 2: 25Za mało!Próba 3: 37Gratulacje! Zgadłeś w 3 próbach!
Lekcja 32 - Komentarze w kodzie
// To jest komentarz jednoliniowy// Można ich używać do krótkich wyjaśnień
/* To jest komentarz wieloliniowy. Można go używać do dłuższych wyjaśnień lub do tymczasowego wyłączania kodu.*/
/// To jest komentarz dokumentacyjny./// Używamy go do dokumentowania kodu,/// który będzie generował dokumentację.////// Możemy używać markdown w komentarzach dokumentacyjnych:/// * Lista punktowana/// * Kolejny punkt////// Możemy też dokumentować parametry:/// @param name Imię użytkownika/// @return Przywitanie użytkownikaString greet(String name) { return "Witaj, $name!";}
// Komentarze są też używane do wyłączania kodu:// print("Ten kod nie zostanie wykonany");print("Ten kod zostanie wykonany");
// Komentarze mogą być używane do wyjaśniania skomplikowanej logiki:// Obliczamy średnią z pominięciem wartości odstającychfinal numbers = [1, 2, 3, 100, 4, 5];final filteredNumbers = numbers.where((n) => n < 10);final average = filteredNumbers.reduce((a, b) => a + b) / filteredNumbers.length;
Lekcja 33 - Podstawy funkcji
// Podstawowa funkcja bez parametrówvoid sayHello() { print("Witaj!");}
// Funkcja z parametremvoid greetPerson(String name) { print("Witaj, $name!");}
// Funkcja zwracająca wartośćint add(int a, int b) { return a + b;}
// Funkcja z wieloma parametramiString createFullName(String firstName, String lastName) { return "$firstName $lastName";}
// Funkcja z wartością domyślną parametruvoid greetWithTitle(String name, [String title = "Pan/Pani"]) { print("Witaj, $title $name!");}
// Funkcja z opcjonalnymi parametrami nazwanymivoid printUserInfo({ required String name, int? age, String? city,}) { print("Imię: $name"); if (age != null) print("Wiek: $age"); if (city != null) print("Miasto: $city");}
void main() { // Wywołanie funkcji bez parametrów sayHello();
// Wywołanie funkcji z parametrem greetPerson("Jan");
// Wywołanie funkcji zwracającej wartość final sum = add(5, 3); print("Suma: $sum");
// Wywołanie funkcji z wieloma parametrami final fullName = createFullName("Jan", "Kowalski"); print("Pełne imię: $fullName");
// Wywołanie funkcji z wartością domyślną greetWithTitle("Kowalski"); // użyje domyślnego tytułu greetWithTitle("Kowalski", "Pan"); // użyje podanego tytułu
// Wywołanie funkcji z opcjonalnymi parametrami nazwanymi printUserInfo( name: "Jan", age: 30, city: "Warszawa", );}
Lekcja 34 - Funkcje strzałkowe i funkcje wyższego rzędu
// Funkcja strzałkowa - skrócona składnia dla prostych funkcjiint multiply(int a, int b) => a * b;
// Funkcja która przyjmuje funkcję jako parametrvoid repeat(int times, void Function() action) { for (var i = 0; i < times; i++) { action(); }}
// Funkcja zwracająca funkcjęFunction multiplyBy(int factor) { return (int number) => number * factor;}
void main() { // Użycie funkcji strzałkowej print(multiply(4, 5)); // 20
// Użycie funkcji jako argumentu do funkcji repeat(3, () => print("Witaj!"));
// Użycie funkcji zwracającej funkcję final multiplyByTwo = multiplyBy(2); print(multiplyByTwo(5)); // 10}
Lekcja 35 - Zaawansowane operacje na kolekcjach
void main() { final numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; final names = ["Anna", "Jan", "Piotr", "Maria", "Kasia"];
// Map - transformacja elementów final doubled = numbers.map((n) => n * 2).toList(); print("Podwojone liczby: $doubled");
// Where - filtrowanie final evenNumbers = numbers.where((n) => n % 2 == 0).toList(); print("Parzyste liczby: $evenNumbers");
// Reduce - redukcja do pojedynczej wartości final sum = numbers.reduce((a, b) => a + b); print("Suma wszystkich liczb: $sum");
// Fold - redukcja z wartością początkową final product = numbers.fold(1, (a, b) => a * b); print("Iloczyn wszystkich liczb: $product");
// Any - sprawdzenie czy jakikolwiek element spełnia warunek final hasEven = numbers.any((n) => n % 2 == 0); print("Czy lista zawiera parzystą liczbę? $hasEven");
// Every - sprawdzenie czy wszystkie elementy spełniają warunek final allPositive = numbers.every((n) => n > 0); print("Czy wszystkie liczby są dodatnie? $allPositive");
// Take - pobranie określonej liczby elementów final firstThree = numbers.take(3).toList(); print("Pierwsze trzy liczby: $firstThree");
// Skip - pominięcie określonej liczby elementów final lastThree = numbers.skip(7).toList(); print("Ostatnie trzy liczby: $lastThree");
// Expand - spłaszczenie listy list final listOfLists = [[1, 2], [3, 4], [5, 6]]; final flattened = listOfLists.expand((list) => list).toList(); print("Spłaszczona lista: $flattened");
// Sort - sortowanie final sortedNames = List<String>.from(names)..sort(); print("Posortowane imiona: $sortedNames");
// Sort z własnym porównaniem final sortedByLength = List<String>.from(names) ..sort((a, b) => a.length.compareTo(b.length)); print("Imiona posortowane po długości: $sortedByLength");
// Chaining - łączenie operacji final result = numbers .where((n) => n % 2 == 0) // tylko parzyste .map((n) => n * n) // podnieś do kwadratu .take(3) // weź pierwsze trzy .toList(); print("Wynik łańcucha operacji: $result");
// GroupBy - grupowanie elementów final words = ["kot", "pies", "krowa", "kura", "koń"]; final groupedByLength = words.fold<Map<int, List<String>>>( {}, (map, word) { map.putIfAbsent(word.length, () => []).add(word); return map; }, ); print("Słowa pogrupowane po długości: $groupedByLength");}
Lekcja 36 - Obsługa błędów
// Własny typ wyjątkuclass ValidationError implements Exception { final String message; ValidationError(this.message);
@override String toString() => 'ValidationError: $message';}
// Funkcja rzucająca wyjątekvoid validateAge(int age) { if (age < 0) { throw ValidationError('Wiek nie może być ujemny'); } if (age > 150) { throw ValidationError('Wiek nie może być większy niż 150'); }}
// Funkcja z obsługą błędówvoid processUserAge(int age) { try { validateAge(age); print('Wiek jest poprawny: $age'); } on ValidationError catch (e) { print('Błąd walidacji: ${e.message}'); } catch (e) { print('Nieoczekiwany błąd: $e'); } finally { print('Wykonano walidację wieku'); }}
// Funkcja z rethrowvoid processWithRethrow(int age) { try { validateAge(age); } catch (e) { print('Przechwycono błąd, przekazuję dalej'); rethrow; // przekazuje błąd dalej }}
void main() { // Przykład poprawnej walidacji processUserAge(25);
// Przykład błędnej walidacji processUserAge(-5);
// Przykład z rethrow try { processWithRethrow(200); } catch (e) { print('Przechwycono błąd w main: $e'); }
// Przykład z finally try { print('Próba wykonania operacji'); throw Exception('Testowy błąd'); } finally { print('Ten kod zawsze się wykona'); }
// Przykład z wieloma blokami catch try { validateAge(-10); } on ValidationError catch (e) { print('Błąd walidacji: ${e.message}'); } on Exception catch (e) { print('Inny błąd: $e'); } catch (e) { print('Nieoczekiwany błąd: $e'); }}
Lekcja 37 - Podstawowe operacje na liczbach
void main() { // Zaokrąglanie double liczba = 3.7; print(liczba.round()); // 4 print(liczba.floor()); // 3 print(liczba.ceil()); // 4
// Wartość bezwzględna int liczbaUjemna = -5; print(liczbaUjemna.abs()); // 5
// Min i max print(num.min(5, 3)); // 3 print(num.max(5, 3)); // 5
// Sprawdzanie czy liczba jest parzysta/nieparzysta int liczba = 4; print(liczba.isEven); // true print(liczba.isOdd); // false
// Konwersja między typami int liczbaCalkowita = 42; double liczbaZmiennoprzecinkowa = liczbaCalkowita.toDouble(); print(liczbaZmiennoprzecinkowa); // 42.0}
Lekcja 38 - Podstawowe operacje na datach
void main() { // Aktualna data i czas final now = DateTime.now(); print(now);
// Tworzenie konkretnej daty final data = DateTime(2024, 3, 15); print(data);
// Dodawanie czasu final jutro = now.add(Duration(days: 1)); print(jutro);
// Odejmowanie czasu final wczoraj = now.subtract(Duration(days: 1)); print(wczoraj);
// Porównywanie dat print(now.isBefore(jutro)); // true print(now.isAfter(wczoraj)); // true
// Formatowanie daty print("${now.day}/${now.month}/${now.year}"); // 15/3/2024 print("${now.hour}:${now.minute}:${now.second}"); // 14:30:45}
Lekcja 39 - Scope (Zakres zmiennych)
// Zmienne globalneString globalVariable = "Jestem zmienną globalną";
void main() { // Zmienne lokalne w funkcji main String localVariable = "Jestem zmienną lokalną w main"; print(localVariable); // Dostęp do zmiennej lokalnej print(globalVariable); // Dostęp do zmiennej globalnej
// Blok kodu tworzy nowy scope { String blockVariable = "Jestem zmienną w bloku"; print(blockVariable); // Dostęp do zmiennej z bloku print(localVariable); // Dostęp do zmiennej z zewnętrznego scope } // print(blockVariable); // Błąd! blockVariable nie jest dostępna poza blokiem
// Przykład z funkcją void innerFunction() { String functionVariable = "Jestem zmienną w funkcji"; print(functionVariable); // Dostęp do zmiennej funkcji print(localVariable); // Dostęp do zmiennej z zewnętrznego scope print(globalVariable); // Dostęp do zmiennej globalnej }
innerFunction(); // print(functionVariable); // Błąd! functionVariable nie jest dostępna poza funkcją
// Przykład z pętlą for (int i = 0; i < 3; i++) { String loopVariable = "Iteracja $i"; print(loopVariable); // Dostęp do zmiennej pętli } // print(loopVariable); // Błąd! loopVariable nie jest dostępna poza pętlą // print(i); // Błąd! i nie jest dostępne poza pętlą
// Przykład z cieniem zmiennych (variable shadowing) String shadowedVariable = "Zewnętrzna wartość"; { String shadowedVariable = "Wewnętrzna wartość"; print(shadowedVariable); // Wypisze "Wewnętrzna wartość" } print(shadowedVariable); // Wypisze "Zewnętrzna wartość"
// Przykład z klasą class Example { String classVariable = "Jestem zmienną klasy";
void method() { String methodVariable = "Jestem zmienną metody"; print(methodVariable); // Dostęp do zmiennej metody print(classVariable); // Dostęp do zmiennej klasy print(globalVariable); // Dostęp do zmiennej globalnej } }
final example = Example(); example.method(); // print(methodVariable); // Błąd! methodVariable nie jest dostępna poza metodą print(example.classVariable); // Dostęp do zmiennej klasy przez instancję}
🎯 Zadanie 4 - Kalkulator dat i liczb
Stwórz program, który:
- Definiuje funkcję
calculateAge
przyjmującą datę urodzenia i zwracającą wiek w latach. - Definiuje funkcję
isLeapYear
sprawdzającą czy rok jest przestępny. - Definiuje funkcję
formatDate
formatującą datę w czytelny sposób. - Definiuje funkcję
calculateDaysBetween
obliczającą liczbę dni między dwiema datami. - Tworzy listę dat urodzenia kilku osób.
- Używa funkcji
map
do obliczenia wieku każdej osoby. - Używa funkcji
where
do filtrowania osób powyżej 18 lat. - Używa funkcji
reduce
do znalezienia najstarszej osoby. - Obsługuje błędy przy niepoprawnych datach.
- Wypisuje wyniki w czytelny sposób.
Przykład oczekiwanego wyniku:
Osoby: [Jan Kowalski (25 lat), Anna Nowak (17 lat), Piotr Wiśniewski (32 lata)]Osoby pełnoletnie: [Jan Kowalski (25 lat), Piotr Wiśniewski (32 lata)]Najstarsza osoba: Piotr Wiśniewski (32 lata)Rok 2024 jest przestępny: trueDni między 2024-01-01 a 2024-12-31: 365
Lekcja 40 - Klasy i obiekty
// Podstawowa definicja klasyclass Person { // Pola klasy String name; int age;
// Konstruktor Person(this.name, this.age);
// Metoda void introduce() { print("Cześć, jestem $name i mam $age lat."); }}
void main() { // Tworzenie obiektu final person = Person("Jan", 25); person.introduce();}
Lekcja 41 - Gettery i settery
class Rectangle { double _width; double _height;
Rectangle(this._width, this._height);
// Getter dla pola width double get width => _width;
// Setter dla pola width set width(double value) { if (value > 0) { _width = value; } else { throw Exception("Szerokość musi być większa od 0"); } }
// Getter obliczający pole powierzchni double get area => _width * _height;}
void main() { final rect = Rectangle(5, 10); print("Pole powierzchni: ${rect.area}");
rect.width = 8; // użycie settera print("Nowa szerokość: ${rect.width}");}
Lekcja 42 - Dziedziczenie
// Klasa bazowaclass Animal { String name;
Animal(this.name);
void makeSound() { print("Jakiś dźwięk..."); }}
// Klasa dziedziczącaclass Dog extends Animal { Dog(String name) : super(name);
@override void makeSound() { print("Hau! Hau!"); }
void wagTail() { print("$name merda ogonem!"); }}
void main() { final dog = Dog("Burek"); dog.makeSound(); // "Hau! Hau!" dog.wagTail(); // "Burek merda ogonem!"}
🎯 Zadanie 5 - Hierarchia zwierząt
Stwórz prostą hierarchię klas:
- Klasa bazowa
Animal
z polemname
i metodąmakeSound()
. - Klasa
Dog
dziedzicząca poAnimal
z getterembreed
i setteremage
. - Klasa
Bird
dziedzicząca poAnimal
z getteremcolor
. - Stwórz instancje i przetestuj dziedziczenie.
- Stwórz listę z kilkoma zwierzęciami i wywołaj w pętli
makeSound()
dla każdego z nich
Przykład:
final dog = Dog("Burek", "Owczarek", 3);final bird = Bird("Stefan", "Kolorowy");dog.makeSound(); // "Hau! Hau!"bird.makeSound(); // "Ćwir ćwir!"print(dog.breed); // "Owczarek"
final lista = [dog, bird, Bird("Dawid", "Różowy")]// tutaj wstaw pętle for
Lekcja 43 - Interfejsy i abstrakcja
// Interfejsinterface class Vehicle { void start(); void stop();}
// Implementacja interfejsuclass Car implements Vehicle { @override void start() { print("Samochód rusza..."); }
@override void stop() { print("Samochód zatrzymuje się..."); }}
// Klasa abstrakcyjnaabstract class Shape { double get area; void draw();}
class Circle extends Shape { final double radius;
Circle(this.radius);
@override double get area => 3.14 * radius * radius;
@override void draw() { print("Rysuję koło o promieniu $radius"); }}
void main() { final car = Car(); car.start(); car.stop();
final circle = Circle(5); print("Pole koła: ${circle.area}"); circle.draw();}
Lekcja 44 - Mixiny
// Mixin z funkcjonalnością lataniamixin Flying { void fly() { print("Lecę wysoko!"); }}
// Mixin z funkcjonalnością pływaniamixin Swimming { void swim() { print("Pływam w wodzie!"); }}
// Klasa używająca mixinówclass Duck with Flying, Swimming { void quack() { print("Kwa kwa!"); }}
void main() { final duck = Duck(); duck.quack(); duck.fly(); duck.swim();}
🎯 Zadanie 6 - Ulepszona hierarchia zwierząt
Rozszerz zadanie 5 o:
- Zmień klasę animal na interfejs lub klasę abstrakcyjną
- Stwórz mixin
Running
z metodąrun()
. - Stwórz mixin
Flying
z metodąfly()
. - Stwórz mixin
Swimming
metodąswim()
- Dodaj mixiny do klas:
Bird
zFlying
,Dog
zRunning
i obydwa zSwimming
. - Przetestuj nowe możliwości.
Lekcja 45 - Generyki
// Klasa generycznaclass Box<T> { T value;
Box(this.value);
T getValue() { return value; }}
// Funkcja generycznaT getFirst<T>(List<T> list) { return list.first;}
void main() { // Użycie klasy generycznej final intBox = Box<int>(42); print(intBox.getValue()); // 42
final stringBox = Box<String>("Hello"); print(stringBox.getValue()); // Hello
// Użycie funkcji generycznej final numbers = [1, 2, 3]; final firstNumber = getFirst(numbers); print(firstNumber); // 1
final words = ["a", "b", "c"]; final firstWord = getFirst(words); print(firstWord); // a}
Lekcja 46 - Generyki z extends
// Ograniczenie typu do num (int lub double)class NumberBox<T extends num> { T value;
NumberBox(this.value);
T getValue() => value;
// Możemy używać operacji matematycznych T multiply(T other) => (value * other) as T;
bool isPositive() => value > 0;}
void main() { // Działa z int (extends num) final intBox = NumberBox<int>(42); print(intBox.multiply(2)); // 84 print(intBox.isPositive()); // true
// Działa z double (extends num) final doubleBox = NumberBox<double>(3.14); print(doubleBox.multiply(2.0)); // 6.28
// Nie zadziała z String (nie extends num) // final stringBox = NumberBox<String>("hello"); // Błąd!
🎯 Zadanie 7 - Funkcja generyczna z ograniczeniami
Stwórz funkcję generyczną feedAnimal<T extends Animal>
, która:
- Przyjmuje zwierzę typu
T
i zwraca to samo zwierzę typuT
. - Funkcja powinna “nakarmić” zwierzę (np. zmienić jego stan, dodać energię).
- Dodaj pole
energy
do klasyAnimal
(int, domyślnie 50). - Funkcja zwiększa energię o 20 i wypisuje komunikat.
- Przetestuj z różnymi typami zwierząt (Dog, Bird).
Przykład:
final dog = Dog("Burek", "Owczarek", 3);final bird = Bird("Stefan", "Kolorowy");
final fedDog = feedAnimal(dog); // "Nakarmiłem Burek! Energia: 70"final fedBird = feedAnimal(bird); // "Nakarmiłem Stefan! Energia: 70"
print(fedDog.energy); // 70print(fedBird.energy); // 70
Lekcja 47 - Enums & switch statement
// Podstawowa definicja enumaenum Direction { north, south, east, west,}
// Enum z wartościamienum HttpStatus { ok(200), notFound(404), serverError(500);
final int code; const HttpStatus(this.code);}
// Enum z metodamienum Color { red, green, blue;
String get hexCode { switch (this) { case Color.red: return '#FF0000'; case Color.green: return '#00FF00'; case Color.blue: return '#0000FF'; } }}
void main() { // Podstawowe użycie enuma Direction direction = Direction.north; print(direction); // Direction.north
// Iteracja po wszystkich wartościach enuma for (var dir in Direction.values) { print(dir); }
// Użycie enuma w switch switch (direction) { case Direction.north: print("Idziemy na północ"); break; case Direction.south: print("Idziemy na południe"); break; case Direction.east: print("Idziemy na wschód"); break; case Direction.west: print("Idziemy na zachód"); break; }
// Użycie enuma z wartościami final status = HttpStatus.ok; print("Kod statusu: ${status.code}"); // 200
// Użycie enuma z metodami final color = Color.red; print("Kod hex koloru: ${color.hexCode}"); // #FF0000
// Porównywanie enumów print(Direction.north == Direction.south); // false print(Direction.north == Direction.north); // true
// Konwersja stringa na enum final directionFromString = Direction.values.firstWhere( (d) => d.toString() == 'Direction.north', orElse: () => Direction.north, ); print(directionFromString); // Direction.north
Lekcja 48 - switch expression
// Switch expressions (Dart 3.0+) String getDirectionName(Direction dir) => switch (dir) { Direction.north => 'Północ', Direction.south => 'Południe', Direction.east => 'Wschód', Direction.west => 'Zachód', };
// Switch expression z pattern matching String getDirectionInfo(Direction dir) => switch (dir) { Direction.north || Direction.south => 'Kierunek północ-południe', Direction.east || Direction.west => 'Kierunek wschód-zachód', };
// Switch expression z guard clauses String getDirectionStatus(Direction dir) => switch (dir) { Direction.north when DateTime.now().hour < 12 => 'Rano idziemy na północ', Direction.north => 'Popołudnie idziemy na północ', Direction.south => 'Idziemy na południe', _ => 'Idziemy w innym kierunku', };
// Przykłady użycia switch expressions print(getDirectionName(Direction.north)); // Północ print(getDirectionInfo(Direction.east)); // Kierunek wschód-zachód print(getDirectionStatus(Direction.north)); // Zależne od pory dnia}
Lekcja 49 - toString() Method
class Person { final String name; final int age; final String city;
Person(this.name, this.age, this.city);
// Podstawowa implementacja toString() @override String toString() { return 'Person(name: $name, age: $age, city: $city)'; }}
Lekcja 50 - Operator == i equals
class Person { final String name; final int age;
Person(this.name, this.age);
// Implementacja operatora == @override bool operator ==(Object other) { // Sprawdzenie czy obiekt jest tego samego typu if (other is! Person) return false;
// Porównanie pól return name == other.name && age == other.age; }
// Implementacja hashCode (wymagana przy nadpisywaniu ==) @override int get hashCode => Object.hash(name, age);}
void main() { // Przykład porównania obiektów final person1 = Person("Jan", 25); final person2 = Person("Jan", 25); final person3 = Person("Anna", 30);
print(person1 == person2); // true - obiekty mają te same wartości print(person1 == person3); // false - obiekty mają różne wartości
// Porównanie z null print(person1 == null); // false
// Porównanie z innym typem print(person1 == "Jan"); // false
// Przykład z kolekcjami final people = [person1, person2, person3]; print(people.contains(person1)); // true print(people.contains(Person("Jan", 25))); // true - obiekt z tymi samymi wartościami
// Przykład z mapą final map = {person1: "Pierwsza osoba"}; print(map[person2]); // "Pierwsza osoba" - klucze są równe}
🎯 Zadanie 8 - Dalszy rozwój naszych zwierzątek
- Dodaj enum
Rozmiar
z wartosciamimały
iduży
- Dodaj pole tego typu do klasy
Animal
- Nadpisz poznane operatory ==, hashCode i toString dla wybranej klasy z poprzednich zadań
Lekcja 51 - Const Constructors
// Klasa z const konstruktoremclass Point { final double x; final double y;
// Const konstruktor - wszystkie pola muszą być final const Point(this.x, this.y);}
void main() { // Tworzenie instancji z const konstruktorem const point1 = Point(1.0, 2.0); const point2 = Point(1.0, 2.0);
// Dwie const instancje z tymi samymi wartościami są identyczne print(point1 == point2); // true
// Bez const tworzone są nowe instancje final point3 = Point(1.0, 2.0); final point4 = Point(1.0, 2.0); print(point3 == point4); // false}
Lekcja 52 - Named Constructors
class Rectangle { final double width; final double height;
// Główny konstruktor Rectangle(this.width, this.height);
// Named constructor - tworzy kwadrat Rectangle.square(double size) : this(size, size);
// Named constructor - tworzy prostokąt z listy wymiarów Rectangle.fromList(List<double> dimensions) : this(dimensions[0], dimensions[1]);
// Named constructor - tworzy prostokąt z mapy Rectangle.fromMap(Map<String, double> map) : this(map['width']!, map['height']!);
double get area => width * height;}
void main() { // Użycie głównego konstruktora final rect1 = Rectangle(5, 10); print("Pole prostokąta: ${rect1.area}");
// Użycie named constructora dla kwadratu final square = Rectangle.square(5); print("Pole kwadratu: ${square.area}");
// Użycie named constructora z listy final rect2 = Rectangle.fromList([3, 4]); print("Pole prostokąta z listy: ${rect2.area}");
// Użycie named constructora z mapy final rect3 = Rectangle.fromMap({'width': 6, 'height': 8}); print("Pole prostokąta z mapy: ${rect3.area}");}
Lekcja 53 - Static, Private and Public Members
class BankAccount { // Public members (domyślnie) String accountNumber; double balance;
// Private members (zaczynają się od _) String _password; List<String> _transactionHistory = [];
// Static members (należą do klasy, nie do instancji) static int totalAccounts = 0; static const double minimumBalance = 100.0;
// Static method static bool isValidAccountNumber(String number) { return number.length == 10; }
// Constructor BankAccount(this.accountNumber, this.balance, this._password) { totalAccounts++; // Zwiększamy licznik kont przy tworzeniu nowego konta }
// Public method void deposit(double amount) { balance += amount; _addTransaction('Deposit: $amount'); }
// Private method void _addTransaction(String transaction) { _transactionHistory.add(transaction); }
// Public method using private members List<String> getTransactionHistory() { return List.unmodifiable(_transactionHistory); }}
void main() { // Użycie statycznych członków klasy print("Minimalny balans: ${BankAccount.minimumBalance}"); print("Liczba kont przed utworzeniem: ${BankAccount.totalAccounts}");
// Tworzenie instancji final account = BankAccount("1234567890", 1000.0, "secret123"); print("Liczba kont po utworzeniu: ${BankAccount.totalAccounts}");
// Użycie publicznych metod account.deposit(500.0); print("Nowy balans: ${account.balance}");
// Użycie statycznej metody print("Czy numer konta jest poprawny: ${BankAccount.isValidAccountNumber('1234567890')}");
// Dostęp do historii transakcji print("Historia transakcji: ${account.getTransactionHistory()}");
// Próba dostępu do prywatnych członków (spowoduje błąd kompilacji) // print(account._password); // Błąd! // account._addTransaction("Test"); // Błąd!}
Lekcja 54 - Factory Constructors i parsowanie JSON
// Klasa reprezentująca użytkownikaclass User { final String id; final String name; final String email; final int age; final List<String> roles;
// Prywatny konstruktor User._({ required this.id, required this.name, required this.email, required this.age, required this.roles, });
// Factory constructor do tworzenia obiektu z JSON factory User.fromJson(Map<String, dynamic> json) { return User._( id: json['id'] as String, name: json['name'] as String, email: json['email'] as String, age: json['age'] as int, roles: List<String>.from(json['roles'] as List), ); }
// Metoda do konwersji obiektu na JSON Map<String, dynamic> toJson() { return { 'id': id, 'name': name, 'email': email, 'age': age, 'roles': roles, }; }
// Factory constructor do tworzenia domyślnego użytkownika factory User.guest() { return User._( id: 'guest', name: 'Gość', email: 'guest@example.com', age: 0, roles: ['guest'], ); }
@override String toString() { return 'User(id: $id, name: $name, email: $email, age: $age, roles: $roles)'; }}
void main() { // Przykład parsowania JSON final jsonData = { 'id': '123', 'name': 'Jan Kowalski', 'email': 'jan@example.com', 'age': 30, 'roles': ['user', 'editor'], };
// Tworzenie obiektu z JSON final user = User.fromJson(jsonData); print('Użytkownik z JSON: $user');
// Konwersja obiektu na JSON final userJson = user.toJson(); print('JSON z obiektu: $userJson');
// Tworzenie gościa final guest = User.guest(); print('Użytkownik gość: $guest');
// Przykład obsługi błędów przy parsowaniu try { final invalidJson = { 'id': '123', 'name': 'Jan Kowalski', // brakuje wymaganych pól }; final invalidUser = User.fromJson(invalidJson); } catch (e) { print('Błąd parsowania: $e'); }}
🎯 Zadanie 9 - Parsowanie Pokemonów
- Stwórz prostą klasę
SimplePokemon
z polaminazwa
iurl
- Stwórz factory kontructor który zamieni poniższą mapę na instancję Pokemona o podanych wartościach:
{"name":"bulbasaur","url":"https://pokeapi.co/api/v2/pokemon/1/"}
Przykład:
final jakasMapa = {"name":"bulbasaur","url":"https://pokeapi.co/api/v2/pokemon/1/"};final jakisPokemon = SimplePokemon.fromJson(jakasMapa);
Lekcja 55 - Asynchroniczność - Future
// Symulacja operacji asynchronicznejFuture<String> fetchData() async { // Symulacja opóźnienia await Future.delayed(Duration(seconds: 2)); return "Dane pobrane!";}
// Obsługa błędów w FutureFuture<String> fetchDataWithError() async { await Future.delayed(Duration(seconds: 1)); throw Exception("Błąd pobierania danych!");}
void main() async { print("Rozpoczynam pobieranie...");
// Użycie await final result = await fetchData(); print(result);
// Obsługa błędów try { await fetchDataWithError(); } catch (e) { print("Wystąpił błąd: $e"); }
// Użycie then fetchData().then((value) { print("Dane pobrane przez then: $value"); });}
Lekcja 56 - Konsumowanie API
import 'package:dio/dio.dart';
void main() async { final dio = Dio();
try { // Prosty GET request final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1'); final data = response.data; print('Tytuł: ${data['title']}'); print('Treść: ${data['body']}'); print('ID użytkownika: ${data['userId']}'); } on DioException catch (e) { print('Błąd Dio: ${e.message}'); } catch (e) { print('Wystąpił błąd: $e'); }}
Ważne uwagi:
- Dodaj do pubspec.yaml:
dependencies: dio: ^5.0.0
- Dio automatycznie parsuje JSON - nie trzeba używać jsonDecode()
- API w przykładzie to JSONPlaceholder - darmowe API do testowania
🎯 Zadanie 10 - Integracja z API Pokemon
- Pobierz odpowiedź z tego endpointa:
https://pokeapi.co/api/v2/pokemon?limit=100000&offset=0
- Sparsuj to na listę
SimplePokemon
z poprzedniego zadania.
Lekcja 57 - Extensions
// Extension na typ Stringextension StringExtension on String { // Metoda zwracająca liczbę słów w stringu int get wordCount => split(' ').length;
// Metoda zwracająca string z pierwszą literą wielką String get capitalize => '${this[0].toUpperCase()}${substring(1)}';
// Metoda zwracająca string z odwróconą kolejnością słów String get reverseWords => split(' ').reversed.join(' ');}
Lekcja 58 - Krotki
void main() { // Podstawowa krotka (tuple) final point = (1, 2); print(point); // (1, 2) print(point.$1); // 1 - dostęp do pierwszego elementu print(point.$2); // 2 - dostęp do drugiego elementu
// Krotka z różnymi typami final mixed = (1, 'hello', true); print(mixed.$1); // 1 print(mixed.$2); // hello print(mixed.$3); // true
// Destrukturyzacja krotki final (x, y) = point; print('x: $x, y: $y'); // x: 1, y: 2
// Użycie w funkcji (int, String) getPersonInfo() { return (25, 'Jan'); }
final (age2, name2) = getPersonInfo(); print('$name2 ma $age2 lat'); // Jan ma 25 lat
// Krotka jako parametr funkcji void printPoint((int, int) point) { print('Punkt: (${point.$1}, ${point.$2})'); }
printPoint((3, 4)); // Punkt: (3, 4)
// Krotka z typami generycznymi (T, U) createPair<T, U>(T first, U second) { return (first, second); }
var pair = createPair<int, String>(1, 'hello'); print(pair); // (1, hello)
// Krotka w kolekcji List<(String, int)> people = [ ('Jan', 25), ('Anna', 30), ('Piotr', 35), ];
for (final (name, age) in people) { print('$name ma $age lat'); }
// Krotka z wartościami null (String?, int?) nullablePair = (null, 42); print(nullablePair); // (null, 42)}
🎯 Zadanie 11 - Krotki i rozszerzenia do Pokemonów
- Napisz rozszerzenie na SimplePokemon które dodaje getter
nameCapitalized
, który zwracaname
, ale z duzej litery. - Dodaj do
SimplePokemon
2 named contructory:SimplePokemon.pikachu
iSimplePokemon.bulbasaur
, a następnie stwórz krotkę z takimi dwoma pokemonami (jak niżej): - Wyprintuj
nameCapitalized
każdego z nich
final pokeKieszen = (SimplePokemon.pikachu(), SimplePokemon.bulbasaur());print(pokeKieszen.$1.nameCapitalized);print(pokeKieszen.$2.nameCapitalized);
Lekcja 59 - Rekordy (nazwane krotki)
void main() { // Podstawowy rekord final person = (name: 'Jan', age: 25); print(person.name); // Jan - dostęp przez nazwę print(person.age); // 25 - dostęp przez nazwę
// Rekord z różnymi typami final user = ( id: 1, name: 'Anna', isActive: true, score: 95.5, ); print('Użytkownik: ${user.name} (ID: ${user.id})'); print('Status: ${user.isActive ? 'Aktywny' : 'Nieaktywny'}'); print('Wynik: ${user.score}');
// Destrukturyzacja rekordu final (name, age) = person; print('$name ma $age lat');
// Rekord w kolekcji final List<({String name, int age})> users = [ (name: 'Jan', age: 25), (name: 'Anna', age: 30), (name: 'Piotr', age: 35), ];
for (final user in users) { print('${user.name} ma ${user.age} lat'); }
// Rekord z wartościami null final ({String? name, int? age}) nullableUser = (name: null, age: 42); print(nullableUser); // (name: null, age: 42)
// Rekord jako zwracana wartość funkcji ({String name, int age}) createUser(String name, int age) { return (name: name, age: age); }
final newUser = createUser('Maria', 28); print('Nowy użytkownik: ${newUser.name}, ${newUser.age} lat');
// Porównywanie rekordów final user1 = (name: 'Jan', age: 25); final user2 = (name: 'Jan', age: 25); print(user1 == user2); // true - rekordy są równe, jeśli mają te same wartości
// Rekord z zagnieżdżonym rekordem final address = (street: 'Kwiatowa', number: 10); final userWithAddress = ( name: 'Jan', age: 25, address: address, ); print('${userWithAddress.name} mieszka przy ${userWithAddress.address.street} ${userWithAddress.address.number}');}
🎯 Zadanie 12 - System zarządzania biblioteką za pomocą dart rekordów
Stwórz system zarządzania biblioteką używając rekordów:
- Stwórz rekord
Book
z polami:title
,author
,year
,isAvailable
- Stwórz rekord
LibraryCard
z polami:cardNumber
,holderName
,expiryDate
- Stwórz rekord
BorrowRecord
z polami:book
,card
,borrowDate
,returnDate
(może być null) - Stwórz listę książek i listę kart bibliotecznych
- Stwórz funkcję
borrowBook
która przyjmujeBook
iLibraryCard
i zwracaBorrowRecord
- Stwórz funkcję
returnBook
która przyjmujeBorrowRecord
i zwraca zaktualizowany rekord zreturnDate
- Przetestuj system - wypożycz i zwróć kilka książek
Przykład użycia:
final book1 = (title: 'Dart dla początkujących', author: 'Szymon Kowaliński', year: 2025, isAvailable: true);final card1 = (cardNumber: '12345', holderName: 'Olcia Dominiak', expiryDate: DateTime(2026, 12, 31));
final borrow = borrowBook(book1, card1);print('Wypożyczono: ${borrow.book.title} dla ${borrow.card.holderName}');
final returned = returnBook(borrow);print('Zwrócono: ${returned.book.title} dnia ${returned.returnDate}');