1145 Wörter
6 Minuten
Flutter - weitere Grundlagen

Scaffold#

  • Hintergrund der App, im Scaffold werden Appbar, Body und BottomNavBar 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 leadingund title
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 und width
  • mittel decoration kann eine BoxDecoration zur Anpassung des Containers hinzugefügt werden
  • wenn decoration vergeben wird, muss auch die color 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 Bild
  • Container oder ClipRRect 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 mittels Align und Positioned 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 oder ScrollView
  • bei einem Child lieber Center oder Align 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 und CrossAxisAlignment 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 oder Column kann man mit flex-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#

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-Stack
  • Navigator.of(context).pushReplacementNamed(routeName) - ersetzt die aktuelle Route mit der definierten
  • Navigator.of(context).pop() - entfernt oberste Route
  • Navigator.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 im build-Widget genutzt werden
  • MediaQuery.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/
Autor
Christian Böhm
Veröffentlicht am
2024-11-19