
Motivation

Development
- Native
- Eine App je für Android/iOs
- Cross Platform
- Eine App, welche irgendwie auf beiden Systemen läuft
- Player:
- React Native
- Ionic
- Flutter
(React) Native vs Flutter

Architektur

Setup
- Flutter
[Get the Flutter SDK, Windows setup[
PS C:\> flutter doctor Doctor summary (to see all details, run ...): [√] Flutter (Channel stable, 2.5.3, on Microsoft ...) [√] Android toolchain - develop for Android ... [√] Chrome - develop for the web [√] Android Studio (version 2020.3) [√] IntelliJ IDEA Ultimate Edition (version 2021.3) [√] Connected device (2 available) - Android Studio Plugins
Dart/Flutter
Hello World
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class HelloWorldApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// abstract
return MaterialApp(
home: Text('Hello World'), // , -> Autoformatter
);
}
}Android
- Einstellungen ->
(Telefoninfo -> Softwareinformation -> )
Buildnummer -> 7 mal tappen
- Developermodus aktiviert ✔️
- USB-Debugging ✔️
Flutter Inspector
Widget Tree


Widget Tree

Widget
- immutable description of UI
- inflated zu
Elements -
build()updated oder deleted und inflated den Subtree neu
Scaffold
Container
Container/Column/Row

ListView

Builder ⟹ Performance
Expanded
StatefulWidget
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Row(
children: [
ElevatedButton(
onPressed: _updateCount,
child: Text('$_count'),
),
],
);
}
void _updateCount() {
setState(() => _count++);
}

UI updated nicht
class Changer extends StatefulWidget {
@override
_ChangerState createState() => _ChangerState();
}
class _ChangerState extends State<Changer> {
String _text = 'Lorem';
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () => _text = 'ipsum',
child: Text('$_text'),
),
],
);
}
kein Update ⟹ setState
widget
class Changer extends StatefulWidget {
final Object data;
Changer(this.data);
@override
_ChangerState createState() => _ChangerState();
}
class _ChangerState extends State<Changer> {
final myOwnData = widget.data;
final myContext = context;
...
}
widget.xgreift auf Variablexim Widget zucontextliefert den Context des Widgets
State Management
Wohin mit dem State?
So tief wie möglich, so hoch wie nötig

TextField fügt zum ListView hinzu
Widget Tree - Element Tree - Render Tree

Widget Lifecycle

? :
Container(
child: input.isEmpty
? Image.asset('assets/images/error.png')
: Text('Yay'),
);
Flex(
children: [
if(isPortrait) Widget(...)
Theme
Text(
'lorem ipsum',
style: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
fontSize: 16,
),
)
don't repeat yourself!
MaterialApp(
theme: ThemeData(
fontFamily: 'Quicksand',
colorScheme: ColorScheme.fromSwatch(
primarySwatch: Colors.blue,
accentColor: Colors.amber,
),
)
);
color: Theme.of(context).colorScheme.primary
Responsiveness

MediaQuery.of(context).
size // width/height
.textScaleFactor
.orientation // portrait/landscape
.viewInsets // onscreen-Keyboard etc.
👍 Speichern für mehr Performance👍
@override
Widget build(BuildContext context) {
final mediaQuery = MediaQuery.of(context);
Verwendung
...children: [
if (mediaQuery.orientation == Orientation.landscape) MyWidget(),
mediaQuery.size.width > 400
? Row(...)
:Widget,
Text(
'text',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20 * mediaQuery.textScaleFactor,
color: Theme.of(context).colorScheme.primary,
),
),
],
LayoutBuilder
Adaptiveness

Revenue

Implementierung
Switch.adaptive(...) // Switch/CupertinoSwitch
Platform.isIOS
? Container()
: FloatingActionButton()
👍 kapseln in MyWidget.adaptive() 👍
Navigation

Top ist sichtbar
Simple Routing
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
OtherPage(data),
),
);
Named Routes
class OtherPage extends StatelessWidget {
static const route = '/other-page';
@override
Widget build(BuildContext context) {
var data = ModalRoute
.of(context)
?.settings
.arguments as Data;
MaterialApp(
// home: const CategoriesPage(),
routes: {
'/': (context) => Home(),
OtherPage.route: (context) => OtherPage(),
},
onUnknownRoute: (settings) {
return MaterialPageRoute(builder: (context) => ErrorPage());
},
Navigator.of(context).pushNamed(
OtherPage.route, arguments: arguments,);
Returning

Tabs
static const _pages = [
PageA(),
PageB(),
];
int _selectedPageIndex = 0;
void _selectPage(int index) {
setState(() => _selectedPageIndex = index);
}
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_selectedPageIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: _selectPage,
currentIndex: _selectedPageIndex,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.category)),
BottomNavigationBarItem(icon: Icon(Icons.star)),],),
State again

State forwarding ⟹ Kompletter Rebuild
$$UI = f(state)$$

Provider
class DataProvider with ChangeNotifier {
final items = <Data>[ ... ];
void addItem(Data data) {
...
notifyListeners();
}
Data findById( ... ) { ... }
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => DataProvider(),
child: MaterialApp( ... )
@override
Widget build(BuildContext context) {
final dataProvider = Provider.of<DataProvider>(context, listen: true);
final data = dataProvider.items;
Advanced
ListView/GridView recyclen Elemente
bzw wenn builder keinen Konstruktor aufruft
ChangeNotifierProvider.value(
value: data,
child: ...,
Feingranularer
Widget build(BuildContext context) {
return Consumer<Data>(
builder: (context, data, child) => FooWidget(foo: data, child: child),
child: NotChanging(), //---^ not rebuilt
);
}