添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
price_tag.dart //價格標籤 product_card.dart //商品卡 products.dart //如果商品陣列大於零,就呼叫ListView.builder的那個列表 ui_elements/ title_default.dart //字體為Oswald的text helpers/ ensure_visible.dart //確認讓input可視的組件

ensure_visible.dart檔案內容

  • pages/auth.dart
  • class _AuthPageState extends State<AuthPage> { final Map<String, dynamic> _formData = { //表單內容 'email': null, 'password': null, 'acceptTerms': false final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); //空的GlobalKey,做表單驗證用 DecorationImage _buildBackgroundImage() { return DecorationImage(...); //產生背景圖 Widget _buildEmailTextField() { return TextFormField( //此組件可以使用validator decoration: InputDecoration( labelText: 'E-Mail', filled: true, fillColor: Colors.white keyboardType: TextInputType.emailAddress, validator: (String value) { if (value.isEmpty || !RegExp(r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?") .hasMatch(value)) { return 'Please enter a valid email'; //若不是email格式或為空,就回傳這個訊息 onSaved: (String value) { _formData['email'] = value; //_formKey.currentState.save被觸發時,存入資料 Widget _buildPasswordTextField() { return TextFormField( decoration: InputDecoration( labelText: 'Password', filled: true, fillColor: Colors.white), obscureText: true, validator: (String value) { if (value.isEmpty || value.length < 6) { return 'Password invalid'; //若長度小於六或為空,就回傳這個訊息 onSaved: (String value) { _formData['password'] = value; Widget _buildAcceptSwitch() { return SwitchListTile( value: _formData['acceptTerms'], onChanged: (bool value) { setState(() { _formData['acceptTerms'] = value; //改變toggle時,就存進去 title: Text('Accept Terms'), void _submitForm() { if (!_formKey.currentState.validate() || !_formData['acceptTerms']) { return; //驗證沒過或acceptTerms是false時就跳出function _formKey.currentState.save(); Navigator.pushReplacementNamed(context, '/products'); //存完後,就跳到商品列表頁面 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Login'), body: Container( decoration: BoxDecoration( image: _buildBackgroundImage(), padding: EdgeInsets.all(10.0), child: Center( child: SingleChildScrollView( child: Container( child: Form( key: _formKey, //他的child都會根據這個key來決定驗證過不過 child: Column( children: <Widget>[ _buildEmailTextField(), _buildPasswordTextField(), _buildAcceptSwitch(), RaisedButton( textColor: Colors.white, child: Text('LOGIN'), onPressed: _submitForm,
  • pages/product_edit.dart
  • import '../widgets/helpers/ensure_visible.dart'; class ProductEditPage extends StatefulWidget { final Function addProduct; //新增商品 final Function updateProduct; //編輯商品 final Map<String, dynamic> product; //商品資訊 final int productIndex; //商品編號 ProductEditPage({this.addProduct, this.updateProduct, this.product, this.productIndex}); @override State<StatefulWidget> createState() { return _ProductEditPageState(); class _ProductEditPageState extends State<ProductEditPage> { final Map<String, dynamic> _formData = { 'title': null, 'description': null, 'price': null, 'image': 'assets/food.jpg' final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final _titleFocusNode = FocusNode(); final _descriptionFocusNode = FocusNode(); final _priceFocusNode = FocusNode(); //新增三個節點,在小鍵盤跳出時,可以抓到位置 Widget _buildTitleTextField() { return EnsureVisibleWhenFocused( focusNode: _titleFocusNode, //節點指定 child: TextFormField( focusNode: _titleFocusNode, //節點參考 decoration: InputDecoration(labelText: 'Product Title'), initialValue: widget.product == null ? '' : widget.product['title'], //初始值是在有傳入product時才有 validator: (String value) { if (value.isEmpty || value.length < 5) { return 'Title is required and should be 5+ characters long.'; //若長度小於5或為空,就回傳這個訊息 onSaved: (String value) { _formData['title'] = value; Widget _buildDescriptionTextField() { return EnsureVisibleWhenFocused( focusNode: _descriptionFocusNode, child: TextFormField( focusNode: _descriptionFocusNode, maxLines: 4, decoration: InputDecoration(labelText: 'Product Description'), initialValue: widget.product == null ? '' : widget.product['description'], //初始值是在有傳入product時才有 validator: (String value) { // if (value.trim().length <= 0) { if (value.isEmpty || value.length < 10) { return 'Description is required and should be 10+ characters long.'; //若長度小於10或為空,就回傳這個訊息 onSaved: (String value) { _formData['description'] = value; Widget _buildPriceTextField() { return EnsureVisibleWhenFocused( focusNode: _priceFocusNode, child: TextFormField( focusNode: _priceFocusNode, keyboardType: TextInputType.number, decoration: InputDecoration(labelText: 'Product Price'), initialValue: widget.product == null ? '' : widget.product['price'].toString(), //初始值是在有傳入product時才有 validator: (String value) { if (value.isEmpty || !RegExp(r'^(?:[1-9]d*|0)?(?:.d+)?$').hasMatch(value)) { return 'Price is required and should be a number.'; //若不為數字或為空,就回傳這個訊息 onSaved: (String value) { _formData['price'] = double.parse(value); //在input裡面要轉成字串,放到變數裡面就轉回數字 void _submitForm() { if (!_formKey.currentState.validate()) { return; //驗證沒過就跳出 _formKey.currentState.save(); //表單內容存入變數 if (widget.product == null) { widget.addProduct(_formData); //新增商品 } else { widget.updateProduct(widget.productIndex, _formData); //編輯商品 Navigator.pushReplacementNamed(context, '/products'); //跳到商品列表 @override Widget build(BuildContext context) { final Widget pageContent = GestureDetector( //一個可以監聽使用者手勢的組件 onTap: () { FocusScope.of(context).requestFocus(FocusNode()); //每次tap時都去找focus節點 child: Container( child: Form( key: _formKey, child: ListView( children: <Widget>[ _buildTitleTextField(), _buildDescriptionTextField(), _buildPriceTextField(), RaisedButton( child: Text('Save'), textColor: Colors.white, onPressed: _submitForm, return widget.product == null ? pageContent : Scaffold( appBar: AppBar( title: Text('Edit Product'), body: pageContent, //編輯狀況時,是有上一頁可以點的