Flutter中RelativeLayout的等价物

时间:2017-06-06 17:18:45

标签: android dart flutter android-relativelayout

有没有办法实现类似于RelativeLayout在Android上的功能?

特别是我正在寻找与centerInParentlayout_below:<layout_id>layout_above:<layout_id>alignParentLeft

类似的内容

有关RelativeLayout的更多参考:https://developer.android.com/reference/android/widget/RelativeLayout.LayoutParams.html

编辑:以下是依赖RelativeLayout

的布局示例

Screenshot

所以在上面的图片中,顶部是豆腐的歌曲&#34;文本在centerInParent内对齐为RelativeLayout。而另外2个是alignParentLeftalignParentRight

在火图标所在的每个单元格上,底部的喜欢数量围绕火焰图标的中心对齐。此外,每个单元格上的顶部和底部标题分别与图像化身的右侧和顶部和底部对齐。

4 个答案:

答案 0 :(得分:164)

Flutter布局通常使用ColumnRowStack窗口小部件的树构建。这些小部件采用构造函数参数来指定子项相对于父项的布局规则,并且还可以通过将它们包含在ExpandedFlexiblePositioned中来影响单个子项的布局。 ,AlignCenter小部件。

也可以使用CustomMultiChildLayout构建复杂的布局。这就是Scaffold在内部实施的方式,并在Shrine demo中显示了如何在应用中使用它的示例。您也可以使用LayoutBuilderCustomPaint,或向下查看图层并展开RenderObject,如sector example所示。像这样手动进行布局是更多的工作,并且在极端情况下产生更多的错误可能性,所以如果可以的话,我会尝试使用高级布局基元。

回答您的具体问题:

  • 使用AppBarleadingtrailing参数来定位您的应用栏元素。如果您想使用Row,请使用mainAxisAlignment MainAxisAlignment.spaceBetween
  • 使用Row crossAxisAlignment CrossAxisAlignment.center mainAxisAlignment将火图标和数字放在下面。
  • 使用Column MainAxisAlignment.spaceBetween 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(), ), ), ], ); } } 来定位您的顶部和底部标题。 (您应该考虑使用ListTile来布置列表切片,但是如果您这样做,您将无法控制确切的定位。)

以下是实现您提供的设计的代码段。在这个例子中,我使用IntrinsicHeight来确定歌曲块的高度,但是你可以通过将它们硬编码到固定的高度来提高性能。

screenshot

AppBar

最后注意事项:在此示例中,我使用了常规CustomScrollView,但您也可以使用SliverAppBar,其中elevation的{​​{1}}为PageView。当它滚动到您的应用栏后面时,这将使内容可见。让这个与dtype('<i8')很好地配合是很棘手的,因为它期望一个固定大小的区域可以自己进入。

答案 1 :(得分:10)

您可以使用Stack,并将其子级设置为PositionedAlign

示例1 (在Positioned中使用Stack

Stack(
  children: <Widget>[
    Positioned(left: 0.0, child: Text("Top\nleft")),
    Positioned(bottom: 0.0, child: Text("Bottom\nleft")),
    Positioned(top: 0.0, right: 0.0, child: Text("Top\nright")),
    Positioned(bottom: 0.0, right: 0.0, child: Text("Bottom\nright")),
    Positioned(bottom: 0.0, right: 0.0, child: Text("Bottom\nright")),
    Positioned(left: width / 2, top: height / 2, child: Text("Center")),
    Positioned(top: height / 2, child: Text("Center\nleft")),
    Positioned(top: height / 2, right: 0.0, child: Text("Center\nright")),
    Positioned(left: width / 2, child: Text("Center\ntop")),
    Positioned(left: width / 2, bottom: 0.0, child: Text("Center\nbottom")),
  ],
)

示例#2 (在Align中使用Stack

Stack(
  children: <Widget>[
    Align(alignment: Alignment.center, child: Text("Center"),),
    Align(alignment: Alignment.topRight, child: Text("Top\nRight"),),
    Align(alignment: Alignment.centerRight, child: Text("Center\nRight"),),
    Align(alignment: Alignment.bottomRight, child: Text("Bottom\nRight"),),
    Align(alignment: Alignment.topLeft, child: Text("Top\nLeft"),),
    Align(alignment: Alignment.centerLeft, child: Text("Center\nLeft"),),
    Align(alignment: Alignment.bottomLeft, child: Text("Bottom\nLeft"),),
    Align(alignment: Alignment.topCenter, child: Text("Top\nCenter"),),
    Align(alignment: Alignment.bottomCenter, child: Text("Bottom\nCenter"),),
    Align(alignment: Alignment(0.0, 0.5), child: Text("Custom\nPostition", style: TextStyle(color: Colors.red, fontSize: 20.0, fontWeight: FontWeight.w800),),),
  ],
);

屏幕截图:

enter image description here

答案 2 :(得分:2)

RelativeLayout包中的AlignPositioned小部件类似,它类似于Android的align_positioned(实际上更强大):

https://pub.dev/packages/align_positioned

来自其文档:

当所需的布局对于“列”和“行”而言过于复杂时, AlignPositioned是真正的救星。 Flutter非常容易组合, 很好,但是有时候翻译的过程不必要地复杂 一些布局要求组合成更简单的小部件。

AlignPositioned对齐,定位,调整大小,旋转和变换 它的子项与容器和子项本身有关。在 换句话说,它可以让您轻松直接地定义 小部件应该相对于另一个出现。

例如,您可以告诉它将其子项的左上角放在 容器左上角的左侧15像素,再加上 将孩子的高度的三分之二移到底部再加上10像素, 然后旋转15度你甚至不知道如何开始这样做 通过组成基本的Flutter小部件?也许可以,但是使用AlignPositioned 这要容易得多,并且只需要一个小部件。

但是,问题中的特定示例非常简单,无论如何我只会使用RowColumn等。 注意:我是这个软件包的作者。

答案 3 :(得分:1)

这里是另一个示例,说明如何将StackPositioned一起使用,以使其像RelativeLayout一样工作。

enter image description here

double _containerHeight = 120, _imageHeight = 80, _iconTop = 44, _iconLeft = 12, _marginLeft = 110;

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.white,
    body: Stack(
      children: <Widget>[
        Positioned(
          left: 0,
          right: 0,
          height: _containerHeight,
          child: Container(color: Colors.blue),
        ),
        Positioned(
          left: _iconLeft,
          top: _iconTop,
          child: Icon(Icons.settings, color: Colors.white),
        ),
        Positioned(
          right: _iconLeft,
          top: _iconTop,
          child: Icon(Icons.bubble_chart, color: Colors.white),
        ),
        Positioned(
          left: _iconLeft,
          top: _containerHeight - _imageHeight / 2,
          child: ClipOval(child: Image.asset("assets/images/profile.jpg", fit: BoxFit.cover, height: _imageHeight, width: _imageHeight)),
        ),
        Positioned(
          left: _marginLeft,
          top: _containerHeight - (_imageHeight / 2.5),
          child: Text("CopsOnRoad", style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500, fontSize: 18)),
        ),
        Positioned.fill(
          left: _marginLeft,
          top: _containerHeight + (_imageHeight / 4),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Column(
                children: <Widget>[
                  Text("2", style: TextStyle(fontWeight: FontWeight.bold)),
                  Text("Gold", style: TextStyle(color: Colors.grey)),
                ],
              ),
              Column(
                children: <Widget>[
                  Text("22", style: TextStyle(fontWeight: FontWeight.bold)),
                  Text("Silver", style: TextStyle(color: Colors.grey)),
                ],
              ),
              Column(
                children: <Widget>[
                  Text("28", style: TextStyle(fontWeight: FontWeight.bold)),
                  Text("Bronze", style: TextStyle(color: Colors.grey)),
                ],
              ),
              Container(),
            ],
          ),
        ),
      ],
    ),
  );
}