1145 Wörter
6 Minuten
Flutter - weitere Grundlagen
Scaffold
- Hintergrund der App, im Scaffold werden
Appbar
,Body
undBottomNavBar
definiert
class Example extends StatelessWidget {
const Example({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
...
);
}
}
Center
- zentriert sein child-Widget
Center(
child: Text('Logo'),
),
Buttons
class CustomButton extends StatelessWidget {
final Function onPressed;
final String text;
final Color buttonColor;
const CustomButton({super.key, required this.onPressed, required this.text, required this.buttonColor});
@override
Widget build(BuildContext context) {
return InkWell(
//onTap: () => print("Custom-Button pressed"),
onTap: () => onPressed(),
child: Material(
elevation: 10,
borderRadius: BorderRadius.circular(8),
child: Container(
height: 30,
width: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: buttonColor),
child: Center(
child: Text(
text,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold)
))),
),
);
}
}
Verwendung des Buttons
CustomButton(onPressed: (){
print("Custom-Button 2 pressed");
}, text: 'Button 2', buttonColor: Colors.yellow)
Appbar
- Standard-Appbar -> mit
leading
undtitle
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color.fromARGB(27, 31, 35, 1),
appBar: AppBar(
leading: const Icon(Icons.home, size: 30),
centerTitle: true,
title: const Text("Widgets"),
foregroundColor: Colors.white,
backgroundColor: const Color.fromARGB(255, 100, 119, 153),
),
body: const example()
);
}
Container
- elementarstes Widget in Flutter
- Größe definierbar mit
height
undwidth
- mittel
decoration
kann eineBoxDecoration
zur Anpassung des Containers hinzugefügt werden - wenn
decoration
vergeben wird, muss auch diecolor
darunter definiert werden (außerhalb führt zu Fehler)
assets
- notwendig, um Dokumente / Bilder nutzen zu können
- Pfad in
pubspec.yaml
definieren
assets:
- assets/images/
images
CircularAvatar
-Widget verwenden für rundes BildContainer
oderClipRRect
für Bild mit beliebiger Form
AnimatedWidgets (AnimatedContainer)
- benötigen immer eine
duration
(gibt Länge der Animation vor) - durch einen
AnimatedController
kann eine Animation in Funktionen steuern curve
-Attribut für Feinschliff
AnimatedPadding
- Padding wird “weich” verschoben
AnimatedPosition
- verschiebt/animiert ein Widget von Position 1 zu Position 2
AnimatedPositioned(
duration: Duration(milliseconds: 400),
top: themeService.isDarkmodeOn ? 100 : 130,
right: themeService.isDarkmodeOn ? 100 : 0,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 200),
opacity: themeService.isDarkmodeOn ? 1 : 0,
child: Beispiel())),
AnimatedOpacity
- lässt ein Widget verblassen oder sichtbarer werden
Positioned(
top: 70,
right: 50,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 400),
opacity: themeService.isDarkmodeOn ? 1 : 0,
child: const Star())),
Stack
Stack
-Widget um Inhalte übereinander zu platzieren- im
Stack
lassen sich Widgets mittelsAlign
undPositioned
ausrichten
child: Stack(
children: [
const SizedBox(
height: 250,
child: CircleAvatar(
backgroundImage: AssetImage("url/to/path.png"),
radius: 200,
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
alignment: Alignment.center,
height: 60,
width: 200,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 0, 165, 206)
.withOpacity(0.9),
borderRadius: BorderRadius.circular(10)),
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Max Mustermann",
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold)),
Text("Webdeveloper",
style: TextStyle(color: Colors.white)),
],
),
),
)
],
),
SizedBox
SizedBox
wird nur über die Größe definiert- dient dazu einen betimmten Platz abzugrenzen oder als Platzhalter in einer
Column
SizedBox(height: 30),
Column
- Zeigt ungeordnete Elemente in einer vertikalen Liste
- nicht scrollbar (ähnlich zu
Row
) - scrollbare Reihe von Widgets benötigt
ListView
oderScrollView
- bei einem Child lieber
Center
oderAlign
zur Ausrichtung verwenden
const Column(
children: <Widget>[
Text('Zeile 1'),
Text('Zeile 2'),
Expanded(
child: FittedBox(
child: FlutterLogo(),
),
),
],
)
Row
- ein Widget, welches untergeordnete Elemente in einer horizontalen Liste anzeigt
- nicht scrollbar (ähnlich zu
Column
) - Children können mit
MainAxisAlignment
undCrossAxisAlignment
ausgerichtet werden
class RowExpanded extends StatelessWidget {
const RowExpanded({super.key});
@override
Widget build(BuildContext context) {
return Row(
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(color: Colors.yellow, height: 100, width: 50),
SizedBox(width: 10),
Expanded(flex: 2, child: Container(color: Colors.green, height: 100)),
SizedBox(width: 10),
Expanded(child: Container(color: Colors.red, height: 100,)),
SizedBox(width: 10,),
Container(color: Colors.yellow, height: 100, width: 50)
]
);
}
}
Expanded
- nimmt den maximal verfügbaren Platz ein
- bei Verwendung mehrerer Expanded-Widgets in einer
Row
oderColumn
kann man mitflex
-Parameter das Verhalten bestimmen
@override
Widget build(BuildContext context) {
return Expanded(
child: Center(
child: Container(
height: 250,
width: 250,
),
),
);
}
Padding
- fügt einen (innen-)Abstand zum Child-Widget hinzu
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), color: Colors.yellow)),
);
}
PageView
- ermöglicht das horizontale scrollen (+ Widgets “rasten ein”)
- durch
PageController(viewPortFraction)
kann definiert werden, ob das nächste Widget schon zu sehen sein soll PageView
muss eine definierte Höhe zugewiesen werden (wenn Scrollbereich unendlich ist)
class PageViewTest extends StatelessWidget {
const PageViewTest({super.key});
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return SizedBox(
height: size.height * 0.2,
child: PageView(
controller: PageController(viewportFraction: 0.95),
children: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SinglePage(
size: size,
title: 'Titel Nr. 1',
text: ' Dies ist ein Text...')),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SinglePage(
size: size,
title: 'Titel Nr. 2',
text: ' Dies ist ein weiterer Text...'))
]));
}
}
class SinglePage extends StatelessWidget {
final Size size;
final String title;
final String text;
const SinglePage(
{super.key, required this.size, required this.title, required this.text});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.grey[700], borderRadius: BorderRadius.circular(15)),
child: Padding(
padding: EdgeInsets.all(30),
child: Column(
children: [
Text(title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20)),
SizedBox(height: 20),
Text(
text,
style: const TextStyle(color: Colors.white, fontSize: 15),
textAlign: TextAlign.center,
)
],
)));
}
}
Material
- ist eine universelles Widget, welches sein Child in die Mittel des Eltern-Widgets positioniert
Material App Widget
- dient dazu, dass Material-App-Komponenten verwendet werden können
SingleChildScrollView
- macht ein
child-Widget
scrollable - Anpassungen des Scroll-Verhaltens mit
physics
möglich - KEIN
Expanded
- oderCenter
-Widget verwenden, da versucht wird, den Platz unendlich auszufüllen siehe auch: https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
Text
- nimmt beliebigen String als
Input
- verfügt über default-Style, kann aber über
style
konfiguriert werden
Named-Routes (Navigator 1)
- Navigator “legt Routes übereinander”
Navigator.of(context).pushNamed(routeName)
legt referenzierte Route über Navigation-StackNavigator.of(context).pushReplacementNamed(routeName)
- ersetzt die aktuelle Route mit der definiertenNavigator.of(context).pop()
- entfernt oberste RouteNavigator.of(context).popUntil(routeName)
- entfernt alle übergeordneten Routes (bis zu ursprünglich referenzierten)
** Navigator 2.0 unterstützt zudem Web und nutzt die nativen (vor- und zurück-Buttons)
MediaQuery
context
ermöglicht Zugang zu allen generellen App-Informationen und kann nur imbuild
-Widget genutzt werdenMediaQuery.of(context).size
gibt aktuelle Screensize zurück
class MediaQueryTest extends StatelessWidget {
const MediaQueryTest({super.key});
@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Container(
height: size.height * 0.1,
width: size.width * 0.8,
color: Colors.yellow
);
}
}
PageView
- PageView expanded über gesamte Fläche -> daher ist eine Begrenzung notwendig, in diesem Fall durch eine übergeordnete SizedBox
Stateful Widget (Simple Counter App)
- damit die App nach einer Statusänderung neu gebaut wird, muss die Methode
setState()
aufgerufen werden
class CounterApp extends StatefulWidget {
const CounterApp({super.key});
@override
State<CounterApp> createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
_counter--;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Counter App"), centerTitle: true, backgroundColor: _counter < 20 ? Colors.grey : Colors.blue),
body: Center(
child: Material(
elevation: 15,
borderRadius: BorderRadius.circular(20),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), color: _counter < 20 ? Colors.grey : Colors.blue
),
height: 200,
width: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Counter"),
SizedBox(height: 20),
Text(
_counter.toString(),
style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
)
],
)),
)),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
FloatingActionButton(
onPressed: () => _decrementCounter(),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: Icon(Icons.remove)),
FloatingActionButton(
onPressed: () => _incrementCounter(),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
child: Icon(Icons.add))
],
),
),
);
}
}
IndexedStack
Widget build(BuildContext context) {
return Scaffold(
//body: _currentIndex == 0 ? WidgetsPage() : CounterApp(), // state geht verloren, da Widget komplett neu gebaut wird
body: IndexedStack( // IndexedStack hält den jeweiligen Status der Seite im Stack vor
index: _currentIndex,
children: const [
BeispielPage_1(),
BeispielPage_2()
],
),
bottomNavigationBar: BottomNavigationBar(
onTap: (index){
setState(() {
_currentIndex = index;
});
},
currentIndex: _currentIndex,
unselectedItemColor: Colors.grey[800],
selectedItemColor: Colors.green,
backgroundColor: Colors.grey[300],
items: const [
BottomNavigationBarItem(icon: Icon(Icons.star), label: "Beispiele"),
BottomNavigationBarItem(icon: Icon(Icons.add), label: "Counter"),
]),
);
}
Flutter - weitere Grundlagen
https://www.couchidee.com/posts/flutter-weitere-grundlagen/