1. MaterialApp
- The first one used in the project is MaterialApp.
MaterialApp is a handy Widget, It encapsulates the widgets needed by the application to achieve Material Design. It is usually used as a top-level Widget. In the MaterialApp, it has the Home property title color Theme routes.
- Properties in the MaterialApp
// The Scaffold component is the basic implementation of the MaterialApp Design layout structure which is provided for display
// API for drawers, snackbar, and sheet
// Scaffold has the following major properties
// AppBar An appbar displayed at the top of the interface
// body The main content Widget displayed in the current interface
// drawer menu control
// onGenerateRoute Transmits the route value and configures the route
Copy the code
2. Route processing
- Unified route management
Create a route file and intercept the route in a unified manner, mainly to process the parameters carried by the route
//
class RouterUtil{
static Route<dynamic>? onGenerateRoute (RouteSettings settings) {//
print("-- -- -- -- -- -- -- -- -- -- -- --");
final String? name = settings.name;
final Function pageContentBuilder = routers[name] as Function;
if(pageContentBuilder ! =null) {
if(settings.arguments ! =null) {
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
return route;
}else{
final Route route = MaterialPageRoute(
builder: (context) =>
pageContentBuilder(context));
returnroute; }}}}Copy the code
3. The component
Everything in Flutter is a component
-
Stateful and stateless components
StatefulWidget and StatelessWidget
import 'package:flutter/material.dart';
import 'package:flutterTanhua/pages/friends/components/RecommendList.dart';
class FansLike extends StatefulWidget {
final arguments;
finalTabController ? tabController;const FansLike({this.tabController, this.arguments});
_FansLikeState createState() => _FansLikeState(arguments: this.arguments);
}
class _FansLikeState extends State<FansLike>
with SingleTickerProviderStateMixin {
Map ? arguments;
TabController ? tabController;
_FansLikeState({this.tabController, this.arguments});
@override
void initState() {
super.initState();
tabController = TabController(length: 3, vsync: this);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
Colors.purple,
Colors.deepOrange,
], begin: Alignment.centerLeft, end: Alignment.centerRight),
),
),
title: TabBar(
indicatorColor: Colors.white,
indicatorSize: TabBarIndicatorSize.label, // The indicator is the type, the label is like this, and the TAB is the entire TAB space
isScrollable: true.// Whether it can slide
indicatorWeight: 3.0.// Height/thickness of indicator
unselectedLabelStyle: TextStyle(fontSize: 16), // No style is selected
labelStyle: TextStyle( fontSize: 24, height: 2), // Select the style
tabs: [
Tab(
child: Text("Pay attention to each other.", style: TextStyle(color: Colors.white),),
// icon: Icon(Icons.recommend),
// text: "recommend ",
),
Tab(
// icon: Icon(Icons.directions_bike),
child: Text("Attention", style: TextStyle(color: Colors.white),),
),
Tab(
// icon: Icon(Icons.directions_bike),
child: Text("Fans", style: TextStyle(color: Colors.white),),
),
],
controller: tabController,
),
),
body: TabBarView(
children: [
Center(child:
Container(
padding: EdgeInsets.all(10),
child: RecommendList(arguments: {"isIcon": "btn"."eachOther": "all"},),
)),
Center(child:
Container(
padding: EdgeInsets.all(10),
child: RecommendList(arguments: {"isIcon": "btn"."eachOther": "like"},),
)
),
Center(child:
Container(
padding: EdgeInsets.all(10),
child: RecommendList(arguments: {"isIcon": "btn"."eachOther": "fans"},),
)
),
],
controller: tabController,
),
);
}
@override
voiddispose() { tabController! .dispose();super.dispose(); }}Copy the code
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false.// Whether to display the debugger
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: Tabs(),
// Configure the route
onGenerateRoute: RouterUtil.onGenerateRoute,
// routes: routers,); }}Copy the code
-
Custom stack sliding components
Reference: blog.csdn.net/qq_39424143…
import 'dart:math';
import 'package:flutter/material.dart';
import '.. /.. /.. /data/SwiperData.dart';
class MySwiper extends StatefulWidget {
final arguments;
const MySwiper({this.arguments}) ;
_MySwiperState createState() => _MySwiperState(arguments: this.arguments);
}
class _MySwiperState extends State<MySwiper> {
var currentPage = images.length - 1.0;
PageController ? controller;
Map arguments;
_MySwiperState({ required this.arguments});
@override
void initState() {
super.initState();
controller = PageController(initialPage: images.length - 1);
// print(controller);controller! .addListener(() { setState(() { currentPage = controller! .page! ; }); }); }@override
Widget build(BuildContext context) {
// TODO: implement build
print(arguments);
return Scaffold(
backgroundColor: Colors.transparent,
body: Center(
child: Stack(
children: <Widget>[
// The two are stacked together. The Controller that slides through PageView controls the page that is currently displayed
CardScrollWidget(currentPage),
Positioned.fill(
child: PageView.builder(
itemCount: images.length,
controller: controller,
reverse: true,
itemBuilder: (context, index) {
returnContainer(); }, ()], (), (), (); }}class CardScrollWidget extends StatelessWidget {
final currentPage;
final padding = 20.0;
final verticalInset = 20.0;
CardScrollWidget(this.currentPage);
@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: (12.0 / 16.0) * 1.2,
child: LayoutBuilder(
builder: (context, contraints) {
var width = contraints.maxWidth;
var height = contraints.maxHeight;
var safeWidth = width - 2 * padding;
var safeHeight = height - 2 * padding;
var heightOfPrimaryCard = safeHeight;
var widthOfPrimaryCard = heightOfPrimaryCard * 12 / 16;
var primaryCardLeft = safeWidth - widthOfPrimaryCard;
var horizontalInset = primaryCardLeft / 2;
List<Widget> cardList = [];
for (int i = 0; i < images.length; i++) {
var leftPage = i - currentPage;
bool isOnRight = leftPage > 0;
var start = padding +
max(
primaryCardLeft -
horizontalInset * -leftPage * (isOnRight ? 15 : 1),
0);
var cardItem = Positioned.directional(
top: padding + verticalInset * max(-leftPage, 0.0),
bottom: padding + verticalInset * max(-leftPage, 0.0) ,
start: start,
textDirection: TextDirection.rtl,
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Container(
decoration: BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.black12,
offset: Offset(3.0.6.0),
blurRadius: 10.0)
]),
child: AspectRatio(
aspectRatio: 12 / 16,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Align(
child: Image.network("${images[i]["header"]}", fit: BoxFit.cover,),
),
Align(
alignment: Alignment.bottomLeft,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Set the title
Padding(
padding: EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
child: Column(
children: <Widget>[
Text(
images[i]["nick_name"],
style: TextStyle(
color: Colors.black,
fontSize: 18,
),
),
Text("${images[i]["marry"]} | ${images[i]["degree"]}| about the same age", textAlign: TextAlign.left),
Padding(
padding:
EdgeInsets.only(left: 12, top: 10),
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 22.0, vertical: 6.0),
decoration: BoxDecoration(
color: Colors.purpleAccent,
borderRadius:
BorderRadius.circular(20.0)),
child: Text("Click to view",
style: TextStyle(color: Colors.white)),
),
)
],
)
),
SizedBox(
height: 10() [() [() [() [() [() [() [() [() cardList.add(cardItem); }returnStack( children: cardList, ); },),); }}Copy the code
-
Universal header assembly
Because some styles need to be customized, using the AppBar feels a bit limited, so just get rid of that and customize it inside the Body and sink it
/// Custom app header, default back to the previous page /// The text displayed with the title: header parameter import 'package:flutter/material.dart'; class CommonHeader extends StatefulWidget { final title; const CommonHeader({this.title}); _CommonHeaderState createState() => _CommonHeaderState(title: this.title); } class _CommonHeaderState extends State<CommonHeader> { Map title; _CommonHeaderState({required this.title}); @override Widget build(BuildContext context) { // TODO: implement build return Container( constraints: BoxConstraints(maxHeight: 80), width: double.infinity, alignment: Alignment.center, decoration: BoxDecoration( image: DecorationImage( image: new ExactAssetImage("images/headbg.png"), fit: BoxFit.cover)), child: Stack( alignment: Alignment.center, children: <Widget>[ Positioned( left: 10, bottom: 15, child: GestureDetector( onTap: () { Navigator.pop(context, true); }, child: Container( alignment: Alignment.center, child: Row( children: <Widget>[ Icon( Icons.arrow_back_ios, color: Colors.white, ) ], ), ), )), Positioned( bottom: 0, child: Container( constraints: BoxConstraints(maxHeight: 50), alignment: Alignment.center, child: Text( "${title["title"]}", style: TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.w500), ), ), ) ], )); }}Copy the code
-
The button component
/// Gradient button assembly /// Parameter: wh: width double /// Parameter HT: height double /// Parameter SRC: background image of the button, used to set the gradient of the button /// Parameter text: The text that the button needs to display import 'package:flutter/material.dart'; typedef OnPressedChangeState(); class LineGradientButton extends StatefulWidget { final OnPressedChangeState ? onPressedChangeState; final arguments; LineGradientButton(this.onPressedChangeState, {this.arguments}); _LineGradientButtonState createState() => _LineGradientButtonState(this.onPressedChangeState, arguments:this.arguments); } class _LineGradientButtonState extends State<LineGradientButton> { Map ? arguments; OnPressedChangeState ? onPressedChangeState; _LineGradientButtonState(this.onPressedChangeState, {this.arguments}); @override Widget build(BuildContext context) { // TODO: implement build print(arguments! ["src"]); returnGestureDetector( child: Container( width: arguments ! ["wd"], height: arguments ! ["ht"].// constraints: BoxConstraints(), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), image: DecorationImage( image: newExactAssetImage(arguments! ["src"]), fit: BoxFit.cover)), child: Text("${arguments! ["text"]}", style: TextStyle(color: Colors.white, fontSize: 18),), ), onTap: onPressedChangeState, ); }}Copy the code
4. Immersive head
/// Dating page top component
/// The Align control can Align the child control in the specified way and adjust its size according to the size of the child control.
/// Expanded component Is a highly used component in the Flutter. It dynamically adjusts the size of the Child component along the main axis, such as filling the remaining space or setting the size ratio. It is often used in combination with a Row or Column.
import 'package:flutter/material.dart';
class Header extends StatefulWidget {
const Header({Key? key}) : super(key: key);
_HeaderState createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
// Create an Icon
List <Widget> _createIcon(){
Color color;
String title = "";
List<Widget> tempList = [SizedBox(width: 50, a)];for(int i = 0; i < 3; i++){
switch (i) {
case 0:
color = Colors.red;
title = "No.";
break;
case 1:
title = "Search the neighborhood.";
color = Colors.blue;
break;
default:
title = "Soul testing.";
color = Colors.deepOrangeAccent;
break;
}
tempList.add(Expanded(
child: GestureDetector(
child: Column(
children: <Widget>[
SizedBox(height: 50,),
Align(
child: Container(
width: 60,
constraints: BoxConstraints(maxHeight: 60),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: color
),
// color: Colors.red,
child: Align(
child: Container(
width: 40,
constraints: BoxConstraints(maxHeight: 40),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: new ExactAssetImage("images/${i}.png"),
fit: BoxFit.cover)),
),
)
),
),
Text("${title}", style: TextStyle(color: Colors.white),)
],
),
onTap: (){
switch (i) {
case 0:
Navigator.pushNamed(context, "/searchFlower");
break;
case 1:
Navigator.pushNamed(context, "/searchNear");
break;
case 2:
Navigator.pushNamed(context, "/testSoul");
break; }}))); } tempList.add(SizedBox(width:50));return tempList;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return
Container(
constraints: BoxConstraints(maxHeight: 160),
width: double.infinity,
decoration: BoxDecoration(
image: DecorationImage(
image: new ExactAssetImage("images/img.png"),
fit: BoxFit.cover)),
child: Row(
children: this._createIcon() ), ); }}Copy the code
5. Problems encountered
-
Container When a Container is nested, the width and height of child components are specified. Why does this function fail?
This is due to the width and height calculation mechanism of the Container, because when the Container calculates the width and height, it not only considers the width and height attributes, but also follows the size constraints of the parent component, called BoxConstraints.
BoxConstraints has four attributes, minWidth, maxWidth, minHeight, and maxHeight. By default, minWidth and maxWidth default to screen width, and minHeight and maxHeight default to screen height.
The parent Constraints the minimum and maximum dimensions of the child components by setting BoxConstraints. If the width and height of the child components are not within the Constraints of the parent Constraints, The dimensions of the child component are forced to conform to the Constraints of the parent component’s Constraints.
The width and height of the child component are set to 50, while the minimum width and height of the parent component constraints are screen width and height respectively. The width and height of the child component does not meet the parent component constraints, so when we set the width and height of the child component, it does not work, so the child component will fill the parent component.
There are many ways to solve this problem, among which the simplest is to wrap the Center component in the outer layer of the child component. Check the source code of the Center component to see that the child component wrapped by the Center component is no longer constrained by the size of the parent component. The Center component inherits from the Align component, so it is possible to nest child components with the Align component.
-
Null check operator used on a null value
This is mainly because I allow the parameter to be null when defining, but do not determine whether it is null when using.
-
RenderFlex children have non-zero flex but incoming height constraints are unbounded.
The reason is that the vertical calculation of the ListView is wrapped around the child View, which means that the child View must have a definite height, or as small a height as possible, not an infinite height. Row is horizontal, and Expanded is used to fill the remaining space in the Row. This does not conflict with ListView and can be used.
Columns are vertical, and using Expanded to fill in the remaining space in the vertical direction would conflict with the ListView, because the ListView would not be able to calculate the height of its children.
I used the ListView as a Colum component in the Container, so I wrapped a layer of Expand for the ListView and solved the problem.
6. Source address
Github.com/Visupervi/F…