最小单元 flutter for web 中的页面布局简介
2020-06-02 本文已影响0人
写一点是一点
在flutter for web中页面本身与手机中进行布局时相比,需要注意web的窗体是可以自由调整大小和比例的。
因此需要特别注意的是当我们在设置一个widget宽高的时候如果使用了计算结果。这个计算结果不要出现随着窗体大小调整可能为负值的情况 否则会出现页面错误。例如
Rect.fromLTWH(50, 10, MediaQuery.of(context).size.width - 50 > 0?MediaQuery.of(context).size.width - 50 :0, MediaQuery.of(context).size.height - 400>0?MediaQuery.of(context).size.height - 400:0)
由于web在电脑中打开空间比较大而在手机中打开又比较小。在做布局的时候可以注意几点
-
沿中轴线向两侧布局。这样在浏览器比较大的是时候保持主要的内容在中心部分。
-
根据宽度可以适配不同的设计。
举个例子

class HomeWidget extends StatefulWidget {
final arguments;
HomeWidget({this.arguments});
@override
State<StatefulWidget> createState() {
return _HomeWidgetState();
}
}
class _HomeWidgetState extends State<HomeWidget> {
bool isShowList = true;
String filePath = "";
String currentFileTitle = "目录";
bool isFirstShow = true;
@override
Widget build(BuildContext context) {
if (widget.arguments != null && isFirstShow) {
this.isShowList = false;
this.filePath = widget.arguments;
this.isFirstShow = false;
}
double mainWith = 800;
double minListWith = 200;
double screenW = MediaQuery.of(context).size.width;
double wLeft = 0;
double wRight = 0;
if (isShowList) {
wRight = 0;
wLeft = screenW > mainWith ? mainWith : screenW;
} else {
wRight = screenW > mainWith ? mainWith : screenW;
if (screenW > mainWith + minListWith * 2) {
wLeft = screenW - mainWith > 600 ? 300 : (screenW - mainWith) / 2;
} else {
wLeft = 0;
}
}
print(widget.arguments.toString());
return Scaffold(
appBar: AppBar(
title: Text(
currentFileTitle,
style: TextStyle(fontSize: 25),
),
backgroundColor: Color(0x00000000),
textTheme: Typography.blackHelsinki,
brightness: Brightness.light,
bottomOpacity: 10,
leading: IconButton(
icon: Icon(Icons.format_list_bulleted),
color: Colors.black,
onPressed: onListBtnClick),
centerTitle: true,
),
body: Flex(
direction: Axis.horizontal,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: wLeft,
padding: EdgeInsets.fromLTRB(0, 0, 5, 0),
//目录
child: FileList(
onSelectModel: onSelectModel,
isSimpleStyle: !isShowList,
),
),
//文章
Container(
width: wRight,
color: Colors.white,
child: FlutterMarkdown(
filePath: filePath,
),
),
Container(
width: isShowList ? 0 : wLeft,
),
]),
);
}
//左侧目录按钮
void onListBtnClick() {
setState(() {
isShowList = true;
if (isShowList) currentFileTitle = "目录";
});
}
void onSelectModel(MarkdownListModel model) {
setState(() {
filePath = model.fileName;
isShowList = false;
currentFileTitle = model.title;
});
}
}
class FlutterMarkdown extends StatelessWidget {
final String filePath;
const FlutterMarkdown({Key key, this.filePath}) : super(key: key);
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: GlobalRequester.instance.requestMarkdown(this.filePath),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Markdown(
data: snapshot.data,
selectable: true,
// styleSheetTheme: MarkdownStyleSheetBaseTheme.cupertino,
physics: BouncingScrollPhysics(),
onTapLink:(url){
GlobalRequester.instance.lunchUrl(url,context);
},
);
} else {
return Center(
child: Text("加载中..."),
);
}
},
);
}
}
class FileList extends StatelessWidget {
final Function(MarkdownListModel) onSelectModel;
final isSimpleStyle;
const FileList({Key key, this.onSelectModel, this.isSimpleStyle})
: super(key: key);
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: GlobalRequester.instance.requestMarkdownModels(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<MarkdownListModel>list = snapshot.data;
return ListView.builder(
scrollDirection: Axis.vertical,
physics: BouncingScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return Container(
width: 200,
height: isSimpleStyle ? 50 : 150,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: isSimpleStyle ? 1 :1,
color: Color(0x66cccccc)))),
child: FlatButton(
onPressed: () => onSelectModel(list[index]),
child: Container(
alignment: Alignment.centerLeft,
child: Text(
list[index].title,
textAlign: TextAlign.left,
style: TextStyle(fontWeight: FontWeight.w700),
),
)));
},
itemCount: list.length,
);
} else {
return Center(
child: Text("加载中..."),
);
}
},
);
}
}
这里我设置标志位切换目录/文章控件的宽度使用flex布局沿水平方向以垂直中轴线位基准排列。添加目录。文章,和右侧一个空白控件用于维持中轴线分布
在显示文章的状态下 左侧的目录和右侧的空白控件保持一致宽度以保持文章在中轴线 。
在宽度不足以展示左侧目录控件的时候设置宽度为0令其隐藏。
在显示目录的状态下将文章和空白控件宽度都设置为0.
页面刷新
在拖动浏览器大小的时候。浏览器这个widget会被setstate因此界面上所有的控件都会重新build。因此不需要接收消息写额外的布局代码。但是需要注意。stateless 控件会被重新创建,那么如果对应有访问网络资源而没有缓存的话在页面拖动的时候会大量的重新请求。

使用statefulwidget或者在statelesswidget 之外自行对资源进行缓存可以避免此类现象。