Flutter - Layouts sind in der Regel gebaut , einen Baum zu verwenden Column
, Row
und Stack
Widgets. Diese Widgets nehmen Konstruktor Argumente , die Regeln für die angeben , wie die Kinder angelegt relativ zum übergeordneten, und Sie können auch das Layout der einzelnen Kinder beeinflussen , indem sie in Einwickeln Expanded
, Flexible
, Positioned
, Align
, oder Center
Widgets.
Es ist auch möglich, komplexe Layouts mit zu erstellen CustomMultiChildLayout
. So wird Scaffold
es intern implementiert, und ein Beispiel für die Verwendung in einer App finden Sie in der Shrine-Demo . Sie können auch LayoutBuilder
oder verwenden CustomPaint
oder eine Ebene nach unten gehen und erweitern, RenderObject
wie im Sektorbeispiel gezeigt . Das manuelle Erstellen Ihrer Layouts ist mehr Arbeit und schafft in Eckfällen mehr Fehlerpotential. Daher würde ich versuchen, mit den übergeordneten Layout-Grundelementen auszukommen, wenn Sie können.
So beantworten Sie Ihre spezifischen Fragen:
- Verwenden Sie die Argumente
leading
und trailing
, AppBar
um Ihre App-Leistenelemente zu positionieren. Wenn Sie Row
stattdessen ein verwenden möchten , verwenden Sie ein mainAxisAlignment
von MainAxisAlignment.spaceBetween
.
- Verwenden Sie ein
Row
mit einem crossAxisAlignment
von, CrossAxisAlignment.center
um das Feuersymbol und die Nummer darunter zu positionieren.
- Verwenden Sie ein
Column
mit einem mainAxisAlignment
von, MainAxisAlignment.spaceBetween
um Ihren oberen und unteren Titel zu positionieren. (Sie sollten in Betracht ziehen ListTile
, die Listenkacheln zu verwenden, verlieren jedoch die Kontrolle über die genaue Positionierung, wenn Sie dies tun.)
Hier ist ein Code-Snippet, das das von Ihnen bereitgestellte Design implementiert. In diesem Beispiel habe ich ein verwendet IntrinsicHeight
, um die Höhe der Song-Kacheln zu bestimmen. Sie können die Leistung jedoch verbessern, indem Sie sie fest auf eine feste Höhe codieren.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
brightness: Brightness.dark,
primaryColorBrightness: Brightness.dark,
),
home: new HomeScreen(),
debugShowCheckedModeBanner: false,
);
}
}
class Song extends StatelessWidget {
const Song({ this.title, this.author, this.likes });
final String title;
final String author;
final int likes;
@override
Widget build(BuildContext context) {
TextTheme textTheme = Theme
.of(context)
.textTheme;
return new Container(
margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
decoration: new BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.3),
borderRadius: new BorderRadius.circular(5.0),
),
child: new IntrinsicHeight(
child: new Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Container(
margin: const EdgeInsets.only(top: 4.0, bottom: 4.0, right: 10.0),
child: new CircleAvatar(
backgroundImage: new NetworkImage(
'http://thecatapi.com/api/images/get?format=src'
'&size=small&type=jpg#${title.hashCode}'
),
radius: 20.0,
),
),
new Expanded(
child: new Container(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(title, style: textTheme.subhead),
new Text(author, style: textTheme.caption),
],
),
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 5.0),
child: new InkWell(
child: new Icon(Icons.play_arrow, size: 40.0),
onTap: () {
// TODO(implement)
},
),
),
new Container(
margin: new EdgeInsets.symmetric(horizontal: 5.0),
child: new InkWell(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Icon(Icons.favorite, size: 25.0),
new Text('${likes ?? ''}'),
],
),
onTap: () {
// TODO(implement)
},
),
),
],
),
),
);
}
}
class Feed extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new ListView(
children: [
new Song(title: 'Trapadelic lobo', author: 'lillobobeats', likes: 4),
new Song(title: 'Different', author: 'younglowkey', likes: 23),
new Song(title: 'Future', author: 'younglowkey', likes: 2),
new Song(title: 'ASAP', author: 'tha_producer808', likes: 13),
new Song(title: '🌲🌲🌲', author: 'TraphousePeyton'),
new Song(title: 'Something sweet...', author: '6ryan'),
new Song(title: 'Sharpie', author: 'Fergie_6'),
],
);
}
}
class CustomTabBar extends AnimatedWidget implements PreferredSizeWidget {
CustomTabBar({ this.pageController, this.pageNames })
: super(listenable: pageController);
final PageController pageController;
final List<String> pageNames;
@override
final Size preferredSize = new Size(0.0, 40.0);
@override
Widget build(BuildContext context) {
TextTheme textTheme = Theme
.of(context)
.textTheme;
return new Container(
height: 40.0,
margin: const EdgeInsets.all(10.0),
padding: const EdgeInsets.symmetric(horizontal: 20.0),
decoration: new BoxDecoration(
color: Colors.grey.shade800.withOpacity(0.5),
borderRadius: new BorderRadius.circular(20.0),
),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List.generate(pageNames.length, (int index) {
return new InkWell(
child: new Text(
pageNames[index],
style: textTheme.subhead.copyWith(
color: Colors.white.withOpacity(
index == pageController.page ? 1.0 : 0.2,
),
)
),
onTap: () {
pageController.animateToPage(
index,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
);
}
);
})
.toList(),
),
);
}
}
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => new _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
PageController _pageController = new PageController(initialPage: 2);
@override
build(BuildContext context) {
final Map<String, Widget> pages = <String, Widget>{
'My Music': new Center(
child: new Text('My Music not implemented'),
),
'Shared': new Center(
child: new Text('Shared not implemented'),
),
'Feed': new Feed(),
};
TextTheme textTheme = Theme
.of(context)
.textTheme;
return new Stack(
children: [
new Container(
decoration: new BoxDecoration(
gradient: new LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
colors: [
const Color.fromARGB(255, 253, 72, 72),
const Color.fromARGB(255, 87, 97, 249),
],
stops: [0.0, 1.0],
)
),
child: new Align(
alignment: FractionalOffset.bottomCenter,
child: new Container(
padding: const EdgeInsets.all(10.0),
child: new Text(
'T I Z E',
style: textTheme.headline.copyWith(
color: Colors.grey.shade800.withOpacity(0.8),
fontWeight: FontWeight.bold,
),
),
)
)
),
new Scaffold(
backgroundColor: const Color(0x00000000),
appBar: new AppBar(
backgroundColor: const Color(0x00000000),
elevation: 0.0,
leading: new Center(
child: new ClipOval(
child: new Image.network(
'http://i.imgur.com/TtNPTe0.jpg',
),
),
),
actions: [
new IconButton(
icon: new Icon(Icons.add),
onPressed: () {
// TODO: implement
},
),
],
title: const Text('tofu\'s songs'),
bottom: new CustomTabBar(
pageController: _pageController,
pageNames: pages.keys.toList(),
),
),
body: new PageView(
controller: _pageController,
children: pages.values.toList(),
),
),
],
);
}
}
Abschließender Hinweis: In diesem Beispiel habe ich eine regelmäßige AppBar
, aber man könnte auch eine Verwendung CustomScrollView
mit einer gepinnten , SliverAppBar
die eine hat elevation
von 0.0. Dadurch wird der Inhalt sichtbar, wenn er hinter Ihrer App-Leiste angezeigt wird. Es ist schwierig, dies zum Spielen zu bringen PageView
, da erwartet wird, dass sich ein Bereich mit fester Größe selbst auslegt.
Sie können verwenden
Stack
und seine Kinder alsPositioned
oder habenAlign
.Beispiel 1 (Verwenden
Positioned
inStack
)Beispiel 2 (Verwenden
Align
inStack
)Bildschirmfoto:
quelle
Hier ist ein weiteres Beispiel, um zu zeigen, wie
Stack
zusammen mitPositioned
verwendet werden kann, damit es funktioniertRelativeLayout
.quelle
Ähnlich wie bei Android
RelativeLayout
(und tatsächlich leistungsfähiger) ist dasAlignPositioned
Widget aus demalign_positioned
Paket:https://pub.dev/packages/align_positioned
Aus seinen Dokumenten:
Das spezifische Beispiel in der Frage ist jedoch recht einfach. Ich würde sowieso nur
Row
s,Column
s usw. verwenden.quelle