养小猫咪的伙伴来我的店铺逛逛吧!抖音商城搜索#早睡早起的猫咪小铺子
Flutter开发日常练习-小猫咪杂货店(新增动画和跳转抖音)_workersJiaDa的博客-CSDN博客URL Launcher是一个Flutter插件,它允许您的应用程序启动网络浏览器、地图应用程序、拨号器应用程序、邮件应用程序等。设定好每个图片的id,通过id作为 'Hero' 组件的标识,id不能重,否则会报错,在这两个页面中必须相同。给列表上的文字加了个缩放的提示动画,感觉很有意思,也有别的效果可以自己试一下。之前的练习加了个详情页面,然后跳转第三方页面抖音用户详情页面。跳转详情页添加了Hero的动画,共享元素过度。https://blog.csdn.net/zxc8890304/article/details/130317615
猫咪杂货铺项目持续更新中
APP设计思路的话改成猫咪百科好了,不会后端,数据源抓包,本地存着了
- 登录注册,个人信息添加本地存储修改
- 国际化版本
- 白天黑夜模式
- 购物车物品删除
- 下单到订单支付流程
- 工具类整理(字体大小,字体颜色,主题颜色等)
- 图片库工具处理
- webview页面拦截优化
- 整体性能优化
- 打包处理
sp_util 是个不错的工具类
flutter_screenutil
在使用的时候需要注册宽度和高度
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 681),
builder: (BuildContext context, Widget? child) => ChangeNotifierProvider(
create: (context) => CarModel(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const StartPage(),
),
),
);
}
}
webview_flutter
添加webview跳转
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebPage extends StatefulWidget {
final String url;
final String title;
const WebPage({super.key, required this.url, required this.title});
@override
State<WebPage> createState() => _WebPageState(this.url, this.title);
}
class _WebPageState extends State<WebPage> {
final String url;
final String title;
late final WebViewController controller;
_WebPageState(this.url, this.title);
@override
void initState() {
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// Update loading bar.
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://pub.dev/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse(url));
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
Expanded(
child: WebViewWidget(controller: controller),
),
],
),
);
}
}
倒计时的广告,使用Timer来倒计时实现,
second_index是设定时间
设定时间自己修改,也可以服务端来修改
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cat_1/pages/intro_page.dart';
import 'package:flutter_cat_1/tools/tools.dart';
import 'package:flutter_cat_1/web/web_page.dart';
import 'package:google_fonts/google_fonts.dart';
import '../main.dart';
class WelcomePage extends StatefulWidget {
const WelcomePage({super.key});
@override
State<WelcomePage> createState() => _WelcomePageState();
}
class _WelcomePageState extends State<WelcomePage> {
int second_index = 3;
int timeIndex = 0;
late Timer timer;
@override
void initState() {
super.initState();
timer = Timer.periodic(
Duration(milliseconds: 50),
(sd) {
setState(() {
timeIndex += 1;
if (timeIndex % 20 == 0) {
second_index -= 1;
}
});
if (timeIndex >= 100) {
_openMain();
}
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.asset(
"assets/IMG_8050.png",
fit: BoxFit.cover,
),
),
Container(
alignment: Alignment.bottomRight,
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
),
alignment: Alignment.center,
margin: const EdgeInsets.all(25),
child: GestureDetector(
onTap: () {
_openMain();
},
child: Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
child: CircularProgressIndicator(
value: timeIndex / 100,
strokeWidth: 2,
),
),
Container(
alignment: Alignment.center,
child: Text(
"跳过",
style: TextStyle(fontSize: 12,color: Colors.white,fontWeight: FontWeight.bold,),
),
),
],
),
),
),
),
Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RichText(
text: TextSpan(
text: "Flutter开发日常练习",
style: GoogleFonts.notoSerif(
color: Colors.black,
fontSize: 17,
),
children: [
TextSpan(
text: "-小猫咪杂货店",
style: GoogleFonts.notoSerif(
color: Colors.black,
fontSize: 17,
// decoration: TextDecoration.underline,
// decorationColor: Colors.blue[800],
)),
],
),
textDirection: TextDirection.ltr,
),
SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: 'CSDN关注',
style: GoogleFonts.notoSerif(
fontSize: 17,
color: Colors.black,
),
children: [
TextSpan(
text: 'workersJiaDa',
style: GoogleFonts.notoSerif(
fontSize: 17,
color: Colors.blue[800],
decoration: TextDecoration.underline,
decorationColor: Colors.blue[800],
fontWeight: FontWeight.bold,
),
recognizer: TapGestureRecognizer()
..onTap = () {
timer.cancel();
Navigator.of(context)
.push(
MaterialPageRoute(
builder: (context) => WebPage(
url:
"https://blog.csdn.net/zxc8890304",
title: "workersJiaDa的博客"),
),
)
.then((value) => reGetTimer());
}),
],
),
),
SizedBox(
height: 10,
),
RichText(
text: TextSpan(
text: '抖音商城搜索',
style: GoogleFonts.notoSerif(
color: Colors.black,
fontSize: 17,
),
children: [
TextSpan(
text: "早睡早起的猫咪小铺子",
style: GoogleFonts.notoSerif(
fontSize: 17,
color: Colors.blue[800],
decoration: TextDecoration.underline,
decorationColor: Colors.blue[800],
fontWeight: FontWeight.bold,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// timer.cancel();
launchURL('snssdk1128://user/profile/88486395468');
},
),
],
),
),
],
),
),
],
),
);
}
void _openMain() {
timer.cancel();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const IntroPage(),
),
(route) => false);
}
void reGetTimer() {
timer = Timer.periodic(
Duration(milliseconds: 50),
(sd) {
setState(() {
timeIndex += 1;
if (timeIndex % 20 == 0) {
second_index -= 1;
}
});
if (timeIndex >= 100) {
_openMain();
}
},
);
}
}
欢迎页的话是
flutter_swiper_plus
非常简陋,简单的实现效果,要重新优化修改
添加了isFirst的判断,第一次打开APP才会显示,
之前做过国际化的项目,海外的需求是
只要是进入欢迎页不点跳过或者进入APP,每次进入APP都会再进入欢迎页面
这样做的话判断的条件需要重新修改一下
import 'dart:isolate';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter_cat_1/welcome/welcome_page.dart';
import 'package:flutter_swiper_plus/flutter_swiper_plus.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:sp_util/sp_util.dart';
class StartPage extends StatefulWidget {
const StartPage({super.key});
@override
State<StartPage> createState() => _StartPageState();
}
class _StartPageState extends State<StartPage> {
bool _isLogin = false;
bool _isFirst = false;
bool _islastPage = false;
int pageInt = 1;
final List _welcomeList = ["welcome_1.png", "welcome_2.png", "welcome_3.png"];
@override
void initState() {
super.initState();
_isFirst = SpUtil.getBool("first")!;
if (_isFirst) {
String? tokenStr = SpUtil.getString('token');
_isLogin = (tokenStr == null || tokenStr.isEmpty) ? false : true;
new Future.delayed(Duration(seconds: 0), () {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => WelcomePage(),
),
(route) => false);
});
}
}
@override
Widget build(BuildContext context) {
return
Container(
color: Colors.white,
height: ScreenUtil().screenHeight,
child: Stack(
children: [
Swiper(
itemBuilder: (context, index) {
return Image.asset(
"assets/${_welcomeList[index]}",
fit: BoxFit.cover,
);
},
itemCount: _welcomeList.length,
loop: false,
onIndexChanged: (index) {
pageInt = index + 1;
if (index == _welcomeList.length - 1) {
setState(() {
_islastPage = true;
});
} else {
setState(() {
_islastPage = false;
});
}
},
),
Container(
width: 100,
height: 40,
alignment: Alignment.center,
margin: EdgeInsets.only(
left: ScreenUtil().screenWidth / 2 - 50,
bottom: 40,
top: ScreenUtil().screenHeight - 80,
right: ScreenUtil().screenWidth / 2 - 50,
),
child: Text(
"$pageInt / 3",
style: const TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
),
Container(
width: 100,
height: 40,
margin: EdgeInsets.only(
left: ScreenUtil().screenWidth / 2 + 100,
bottom: 40,
top: ScreenUtil().screenHeight - 80,
// right: ScreenUtil().screenWidth / 2 - 10,
),
// decoration: BoxDecoration(
// border: Border.all(color: Colors.blue[900], width: 2),
// borderRadius: BorderRadius.circular(8),
// ),
child: Center(
child: TextButton(
onPressed:() => _jumpWelcome(),
// Navigator.push(context, MaterialPageRoute(
// builder: (context) {
// },
// ));
child: _islastPage
? Text(
"立即进入",
style: GoogleFonts.notoSerif(
fontSize: 17,
color: Colors.blue[700],
fontWeight: FontWeight.bold),
)
: Text(
"跳过",
style: GoogleFonts.notoSerif(
fontSize: 17,
color: Colors.blue[700],
fontWeight: FontWeight.bold),
),
),
),
)
],
),
);
}
_jumpWelcome() {
SpUtil.putBool('first',true);
String? tokenStr = SpUtil.getString("token");
_isLogin = (tokenStr == null || tokenStr.isEmpty) ? false : true;
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => WelcomePage(),
),
(route) => false);
}
}
侧滑栏也是,比较简陋
import 'package:flutter/material.dart';
import 'package:flutter_cat_1/welcome/start_page.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class DrawerPage extends StatelessWidget {
const DrawerPage({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
elevation: 300,
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text("铲屎官铲屎官铲屎官"),
accountEmail: Text("饲养员饲养员饲养员"),
currentAccountPicture: CircleAvatar(
backgroundImage: AssetImage("assets/IMG_9406.png"),
),
arrowColor: Colors.red,
onDetailsPressed: () {},
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.5),
offset: Offset(10, 10),
blurRadius: 45,
spreadRadius: 0.0,
),
],
image: DecorationImage(
image: AssetImage(
"assets/IMG_0259.png",
),
fit: BoxFit.cover),
),
),
Expanded(
child: ListView(
padding: EdgeInsets.symmetric(vertical: 10),
children: <Widget>[
ListTile(
title: Text('个人中心'),
),
Divider(),
ListTile(
title: Text('我的小猫咪'),
),
Divider(),
ListTile(
title: Text("我的订单"),
),
Divider(),
ListTile(
title: Text("关于我们"),
),
Divider(),
],
),
),
],
),
Positioned(
bottom: 20,
right: 20,
child: InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
GestureDetector(
onTap: () => Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) {
return StartPage();
},
), (route) => false),
child: MenuItemButton(
child: Column(
children: const <Widget>[
Icon(
Icons.power_settings_new_outlined,
color: Colors.red,
),
Text(
"退出登录",
style: TextStyle(
color: Colors.red,
fontSize: 12,
),
),
],
)),
),
],
),
),
),
],
),
);
}
}
DEMO地址
GitHub - HelloJiada/flutter_cat_1Contribute to HelloJiada/flutter_cat_1 development by creating an account on GitHub.https://github.com/HelloJiada/flutter_cat_1.git