flutter页面练习_1

2020-09-22  本文已影响0人  Eason_0cce

熟能生巧,跟着老外写页面第1篇

完成效果:


页面截图.gif

项目结构:


image.png

mian.dart:

import 'package:flutter/material.dart';
import 'package:eat/ui/profile_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ProfileScreen(),
    );
  }
}

profile_screen.dart:

import 'package:flutter/material.dart';
import 'package:eat/model/meal.dart';
import 'package:vector_math/vector_math_64.dart' as math;
import 'package:eat/ui/WorkoutScreen.dart';
import 'package:page_transition/page_transition.dart';

class ProfileScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final height = MediaQuery.of(context).size.height;
    final width = MediaQuery.of(context).size.width;
    return Scaffold(
      backgroundColor: const Color(0xFFE9E9E9),
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            Positioned(
              top: 0,
              left: 0,
              height: height*0.3,
              right: 0,
              child: ClipRRect(
                borderRadius: const BorderRadius.vertical(
                  bottom: const Radius.circular(40),
                ),
                child: Container(
                  padding: EdgeInsets.only(left: 20, right: 20),
                  color: Colors.white,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      ListTile(
                        title:  Text("Date Year", style: TextStyle(
                          fontWeight: FontWeight.w400,
                          fontSize: 14
                        ),),
                        subtitle: Text('Hello Eason',style: TextStyle(
                            fontWeight: FontWeight.w800,
                            fontSize: 16,
                          color: Colors.black
                        )),
                        trailing: ClipOval(child: Image.asset('images/head.jpg')),
                      ),
                      Row(
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: <Widget>[
                          _RadialProgress(width: width*0.4, height: width*0.4,),
                          Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            mainAxisAlignment: MainAxisAlignment.start,
                            children: <Widget>[
                              _IngredientProgress(
                                ingredient: "Protein",
                                progress: 0.3,
                                width: width*0.3,
                                progressColor: Colors.green,
                                leftAmount: 72,
                              ),
                              _IngredientProgress(
                                ingredient: "Carbs",
                                width: width*0.3,
                                progress: 0.2,
                                progressColor: Colors.red,
                                leftAmount: 252,
                              ),
                              _IngredientProgress(
                                ingredient: "Protein",
                                width: width*0.3,
                                progress: 0.1,
                                progressColor: Colors.yellowAccent,
                                leftAmount: 61,
                              )
                            ],
                          )
                        ],
                      )
                    ],
                  ),
                ),
              ),
            ),
            Positioned(
              top: height*0.32,
              left: 0,
              right: 0,
              child: Container(
                height: height * 0.52,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.only(
                        bottom: 8,
                        left: 32,
                        right: 16
                      ),
                      child: Text(
                        "MEALS FOR TODAY",
                        style:const TextStyle(
                          color: Colors.blueGrey,
                          fontSize: 16,
                          fontWeight: FontWeight.w700
                        ),
                      ),
                    ),
                    Expanded(
                      child: SingleChildScrollView(
                        scrollDirection: Axis.horizontal,
                        child: Row(
                          children: <Widget>[
                            SizedBox( width:30 ),
                            _MealCard(meal: meals[0]),
                            _MealCard(meal: meals[1]),
                            _MealCard(meal: meals[2])
                          ],
                        ),
                      )
                    ),
                    SizedBox(height: 20,),
                    Expanded(
                      child: GestureDetector(
                          onTap: (){
                            Navigator.push(context, PageTransition(type: PageTransitionType.scale, alignment:Alignment(0, 0.5), child: WorkoutScreen()));
                          },
                          child: Container(
                            margin: EdgeInsets.only(
                              left: 32,
                              right: 32,
                              bottom: 10
                            ),
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(30),
                              gradient: LinearGradient(
                                begin: Alignment.topCenter,
                                end: Alignment.bottomCenter,
                                colors: [
                                  const Color(0xFF20008B),
                                  const Color(0xFF200087)
                                ]
                              )
                            ),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: <Widget>[
                                Padding( padding: EdgeInsets.only(left: 20, top: 10), child: Text('Some Text', style: TextStyle( color:Colors.white70, fontSize: 16),)),
                                Padding( padding: EdgeInsets.only(left: 20, top: 5, bottom: 10), child: Text('Some Text2', style: TextStyle( color:Colors.white, fontSize: 18, fontWeight: FontWeight.w700),)),
                                Padding(
                                  padding: EdgeInsets.only(left: 20, right: 20),
                                  child: Row(
                                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                    children: <Widget>[
                                      Container(
                                          child: Icon(Icons.music_video, color: Colors.white, size: 40, ),
                                          width: 60,
                                          height: 60,
                                          decoration: BoxDecoration(
                                              color: Colors.white.withOpacity(0.6),
                                            borderRadius: BorderRadius.circular(6)
                                          ),
                                      ),
                                      Container(
                                        child: Icon(Icons.face, color: Colors.white, size: 40, ),
                                        width: 60,
                                        height: 60,
                                        decoration: BoxDecoration(
                                            color: Colors.white.withOpacity(0.6),
                                            borderRadius: BorderRadius.circular(6)
                                        ),
                                      ),Container(
                                        child: Icon(Icons.movie, color: Colors.white, size: 40, ),
                                        width: 60,
                                        height: 60,
                                        decoration: BoxDecoration(
                                            color: Colors.white.withOpacity(0.6),
                                            borderRadius: BorderRadius.circular(6)
                                        ),
                                      ),Container(
                                        child: Icon(Icons.person, color: Colors.white, size: 40, ),
                                        width: 60,
                                        height: 60,
                                        decoration: BoxDecoration(
                                            color: Colors.white.withOpacity(0.6),
                                            borderRadius: BorderRadius.circular(6)
                                        ),
                                      ),
                                    ],
                                  ),
                                )
                              ],
                            ),
                          ),
                        ),
                    )
                  ],
                ),
              ),
            )
          ],
        ),
      ),
      bottomNavigationBar: ClipRRect(
        borderRadius: BorderRadius.vertical(
          top: const Radius.circular(40)
        ),
        child: BottomNavigationBar(
          iconSize: 40,
          selectedIconTheme: IconThemeData(
            color: const Color(0xff200087),
          ),
          unselectedIconTheme: IconThemeData(
            color: Colors.black12,
          ),
          items: [
            BottomNavigationBarItem(
              icon: Icon(
                Icons.home
              ),
              title: Text('', style: TextStyle( color: Colors.white),)
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.search
              ),
              title: Text('', style: TextStyle( color: Colors.white),)
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.person
              ),
              title: Text('', style: TextStyle( color: Colors.white),)
            )
          ],
        ),
      ),
    );
  }
}

class _MealCard extends StatelessWidget {
  final Meal meal;
  const _MealCard({Key key, @required this.meal}):super(key : key);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      margin:const EdgeInsets.only(right: 20, bottom: 10),
      child: Material(
        borderRadius: BorderRadius.all(Radius.circular(20)),
        elevation: 4,
        child: Column(
          mainAxisSize: MainAxisSize.max,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Flexible(
              fit: FlexFit.tight,
              child: ClipRRect( 
                  borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
                  child: Image.asset(
                      meal.imagePath,
                      width: 150,
                      fit: BoxFit.fill,
                  )
              ),
            ),
            Flexible(
              fit: FlexFit.tight,
              child: Padding(
                padding: const EdgeInsets.only(left: 8.0),
                child: Column(
                  crossAxisAlignment:CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    SizedBox(height: 2,),
                    Text(meal.mealTime, style:TextStyle( fontWeight: FontWeight.w500, fontSize: 14, color:  Colors.blueGrey),),
                    Text(meal.name, style:TextStyle( fontWeight: FontWeight.w500, fontSize: 14, color:  Colors.black)),
                    Text(meal.kiloCaloriesBrunt, style:TextStyle(  fontSize: 14, color:  Colors.blueGrey)),
                    Text(meal.timeTaken, style:TextStyle(  fontSize: 14, color:  Colors.blueGrey)),
                    SizedBox(
                      height: 16,
                    )
                  ],
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

class _RadialProgress extends StatelessWidget{
  final double width, height;
  const _RadialProgress({Key key, this.width, this.height}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return CustomPaint(
      painter: _RadioPainter(progress:0.7),
      child: Container(
        width: width,
        height: height,
        child: Center(
          child: RichText(
            textAlign: TextAlign.center,
            text: TextSpan(
              children: [
                TextSpan(
                  text: "1731",
                  style: TextStyle(
                    fontSize: 26,
                    fontWeight: FontWeight.w700,
                    color: const Color(0xFF200087)
                  )
                ),
                TextSpan(text:'\n'),
                TextSpan(
                    text: "some text",
                    style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.w500,
                        color: const Color(0xFF200087)
                    )
                )
              ]
            ),
          ),
        ),
      ),
    );
  }
}

class _RadioPainter extends CustomPainter{
  final double progress;
  _RadioPainter({this.progress});
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
        ..strokeWidth = 10
          ..color = Color(0xFF200087)
          ..style = PaintingStyle.stroke
          ..strokeCap = StrokeCap.round;
    Offset center = Offset(size.width / 2, size.height / 2);
    double relativeProgress = 360 * progress;
//    canvas.drawCircle(center, size.width/2, paint);
    canvas.drawArc(
        Rect.fromCircle(center: center, radius:  size.width/2),
        math.radians(-90),
        math.radians(relativeProgress),
        false,
        paint
    );
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}

class  _IngredientProgress extends StatelessWidget{
  final String ingredient;
  final double leftAmount, width;
  final double progress;
  final Color progressColor;
  const _IngredientProgress({Key key, this.ingredient, this.leftAmount, this.width, this.progress, this.progressColor}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        Text( ingredient, style: TextStyle(
          fontSize: 14,
          fontWeight: FontWeight.w700
        )),
        Row(
          children: <Widget>[
            Container(
              height: 10,
              width: width,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.all(Radius.circular(5)),
                color: Colors.black12,
              ),
              child: Align(
                child: Container(
                  height: 10,
                  width: width*progress,
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.all(Radius.circular(5)),
                    color: progressColor,
                  ),
                ),
                alignment: Alignment.topLeft,
              ),
            ),
            Text("${leftAmount}", style: TextStyle(color: Colors.blueGrey),)
          ],
        )
      ],
    );
  }
}

WorkoutScreen.dart:

import 'package:flutter/material.dart';

class WorkoutScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      backgroundColor: const Color(0xFF20008B),
      body: Container(
        alignment: Alignment.topCenter,
        padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
        child: GestureDetector(
            onTap: (){ Navigator.of(context).pop(); },
            child: Icon( Icons.close, size: 50, color: Colors.white, )
        ),
      ),
    );
  }
}

pubspec.yaml:

name: eat
description: A new Flutter application.

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  page_transition: ^1.1.6

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2

dev_dependencies:
  flutter_test:
    sdk: flutter


# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - images/fruit.jpg
    - images/head.jpg
  #  - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

fruit.jpg:


fruit.jpg

head.jpg:自己找

独家编程小技巧:


image.png

_MealCard 内部定义急接参meal之后,可以选中meal,按住alt+enter出现自动补全构造语法,以前的我是死记硬背的,后来直接释放了这部分记忆用来学习

知识点:
1.绘制圆弧
2.便捷的圆形头像
3.路由动画

补充说明:页面借鉴于https://www.youtube.com/watch?v=DCskd6_GJtY

上一篇 下一篇

猜你喜欢

热点阅读