原作者:Mariano Zorrilla
你在大公司工作吗?
我知道……你有没有想过(不止一次)“哦,天哪……早上9点,我的一天已经被会议和调bug挤满了!"
是的,几乎每天!
我敢肯定,每次你在无聊的会议中都会去刷朋友圈!(同意的点赞)
1.我从哪里开始(问题):
我知道每个人在家里都有很多时间做自己的事情,对吧?比如看电视节目、电影、社交媒体……
对于那些有足够财富的人来说,通过聊天、电子邮件和会议交流的唯一途径是什么?
2.研究:
我们都喜欢探索,对吧?
所以,我想“我怎样才能把探索和实际产品结合起来”。
3.工作时间:
好吧,我有8个小时的“办公室工作”。所以,我没有太多的Web知识,因为我主要是一个移动开发人员,现在他们做了更多的管理工作(我知道双方的痛苦),但我知道Flutter 和Flutter Web目前处于测试版,但,你懂的…!
4.命名产品:
首先,你要为你的产品想一个好名字!
5.工作,工作,工作 !
利用我的空闲时间开始一个新项目总是很有趣的,对吧?但这一次看起来真的很有潜力,范围也没那么大。
选择的工具?!
为什么?因为开始一个新的项目、设计和实际完成都很容易!
它的发展实在是太快了:身为移动开发者,Web对我来说有点不同,即使我们每天都在使用它,但我们需要考虑的路线(路径)比以往任何时候都多!不仅仅是深层次的联系。
我们在纽约和西蒙以及弗雷特纽约的朋友们开完会后,我突然想到了一个主意。
我的解决方案提供了一个小型的StatefulWidget导航器,没有什么新奇之处,也不是世界上最干净的工作:
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TMSBAE',
onGenerateRoute: (settings) => NavigatorRoute.route(settings.name),
);
}
}
每一条路径都将来自onGenerateRoute,然后将那个坏小子解析到我们的导航器中。
一旦我走上这条路,我就使用了核心机器学习……或者简称If/Else。
@override
void initState() {
super.initState();
Future.delayed(Duration(milliseconds: 0), () {
if (widget.path == '/') {
Navigator.of(context).pushAndRemoveUntil(HomeScreen.route(), (_) => false);
return;
} else if (widget.path == '/join') {
Navigator.of(context).pushAndRemoveUntil(JoinScreen.route(), (_) => false);
return;
} else if (widget.path.contains('/join/')) {
Navigator.of(context).pushAndRemoveUntil(JoinScreen.routeCode(widget.path.split('/')[2]), (_) => false);
return;
} else if (widget.path == '/bae') {
Navigator.of(context).pushAndRemoveUntil(BaeScreen.route(), (_) => false);
} else if (widget.path.contains('/result/')) {
Navigator.of(context).pushAndRemoveUntil(ResultScreen.routeCode(widget.path.split('/')[2]), (_) => false);
} else if (widget.path == '/about') {
} else {
Navigator.of(context).pushAndRemoveUntil(HomeScreen.route(), (_) => false);
return;
}
});
}
} else {
Navigator.*of*(context).pushAndRemoveUntil(HomeScreen.*route*(), (_) => false);
return;
}
});
}
这样做的目的是避免获得大量的页面,例如:
{URL}/join/djUdmd19m4
Will open:
- “/” 主屏幕
- “/join” join屏幕
- “/join/djUdmd19m4” 我们要打开的实际屏幕。
这是Flutter开发人员使用常规onGenerateRoute方法最常见的问题。
6.设计:
我很幸运,我也是一个UI/UX设计/开发人员……所以,从技术上说,我是一个全栈开发人员?!
我查看了一些设计网站,以获得灵感,并知道更多与我的设计,调色板等…我也看了一些开放源码字体使用和废话。
但它们只有一/两个屏幕大小,没有什么新奇之处,没有数据库/业务逻辑,只是一个使用动画的简单而酷的例子。
7.编码:
再说一次,即使我有空闲时间,记住,我有一份全职工作,唯一的空闲时间是我日常工作之外的时间……但足以让这个新项目迅速完成。
做网站的时候,总有一个大字是“响应”的是的,拜托!记住这个!我们有智能手机、笔记本电脑、台式机、平板电脑等……您的设计应该在每一个屏幕上都是实用/有用的!。
SingleChildScrollView
是其中一个你会经常使用的小部件,如果你计划有动态列表的话就不是最有用的,因为这个小部件会把所有东西都加载到内存中。
MediaQuery
为了得到尺寸和方位,超级有用,现在每个人都应该知道,但是…是的。
LayoutBuilder
太棒了!在考虑不同的形状因素和屏幕时,这一个起到了很好的带头作用。
Screens
我有几个:登录页(主屏幕),创建屏幕,加入屏幕,结果屏幕,投票屏幕,关于屏幕。
我在屏幕之间移动的整个业务逻辑是通过使用:
Navigator.*of*(context).pushAndRemoveUntil
我不想为这个产品保留一个堆栈,因为我可以用我的AppBar和屏幕上的一些其他元素/按钮在屏幕之间移动,拜托,它变得对PWA更友好了。
PWA
是的,flutter支持PWA和否,这两者并不是不同的东西,它们只是两者之间的一个补充。
如果你使用的是Flutter v.1.5+,请确保运行“Flutter create.”来获取丢失的文件。如果由于任何原因您没有它,让我给您以下片段:
在index.html中<body>
:
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
在您的/web文件夹上,您应该有一个manifest.json文件,如果没有,它如下所示:
{
"name": "App Name",
"short_name": "App Short Name",
"start_url": ".",
"display": "standalone",
"background_color": "#A83EF6",
"theme_color": "#A83EF6",
"description": "App Description",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
注意那边的一些元素:
"display": "standalone"
允许您拥有全屏/本地的外观和感觉。您可以在这里阅读更多信息
你的图标应该在/web/icons(或任何其他文件夹中,但这样看起来不错)中,并且将是你的主屏幕/应用程序抽屉中使用的图标。
对于我那疯狂又好看的header,我用了:
ClipPath
使用以下代码段:
class HeaderWaveClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
var path = Path();
path.lineTo(0, 0);
path.lineTo(0, size.height - 100);
path.cubicTo(
0,
size.height - 90,
size.width * 0.004,
size.height - 70,
size.width * 0.06,
size.height - 75,
);
path.cubicTo(
size.width * 0.07,
size.height - 75,
size.width * 0.14,
size.height - 95,
size.width * 0.12,
size.height - 65,
);
path.cubicTo(
size.width * 0.12,
size.height - 65,
size.width * 0.08,
size.height - 10,
size.width / 3 * 0.98,
size.height - 40,
);
path.cubicTo(
(size.width / 3) * 1.08,
size.height - 40,
(size.width / 3) - 60,
size.height - 5,
(size.width / 3) + 100,
size.height - 5,
);
path.cubicTo(
(size.width / 3) * 1.8,
size.height - 5,
(size.width / 2) * 1.6,
size.height,
(size.width / 2) * 1.52,
size.height - 30,
);
path.cubicTo(
(size.width / 2) * 1.52,
size.height - 35,
(size.width / 2) * 1.4,
size.height - 60,
size.width - (size.width * 0.05),
size.height - 60,
);
path.cubicTo(
size.width - (size.width * 0.05),
size.height - 60,
size.width,
size.height - 60,
size.width,
size.height - 90,
);
path.lineTo(size.width, 0);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
我花了几个小时来解决这个问题……数学很有趣,但是……是的,花了几个小时让它在每一个屏幕上都反应灵敏,看起来很漂亮。
我说过每件事都花了我大约12个小时的编码,而其中很多时间是在2天的工作框架内进行设计+测试。
在Firebase上托管花了我几秒钟的时间(字面上),而且由于G域的存在,使用我自己的域非常简单。
8. 裂变传播?(希望如此)
我知道有一个特定的网站有我需要的东西来发布我的新创作,那就是Product Hunt - The best new products in tech.
每一个新的/酷的/怪异的产品/项目都结束了,我想知道那是什么感觉!我想创建我的第一个病毒产品后,这么多年的编码没有许多酷/有趣的想法。
这个过程相当直接:
- 创建帐户
- 等待1周(或关注他们的时事通讯以获得即时访问)
- 发布产品
- 关注每个细节(标题、描述、图标、截图/视频等)
- 发布!(你可以安排这个)
最后的想法
这真的很有趣,希望人们能经常使用它!我知道这只是一个迷因,但这个有潜力,也许团队会在他们的日历中包含我自动生成的链接,这样每个人都有机会决定会议是否应该是电子邮件。
PS:我是黑胡桃实验室社区的一名成员,最近经常在看一些老外做的有趣的人工智能项目,如果有兴趣或疑问可以在评论区留言或私信与我交流鸭~