2017-11-13 9 views
0

ListView 왼쪽으로 드래그하려는 항목이 삭제 버튼이 제대로 작동하는 것처럼 보입니다. 문제는 내가 삭제 버튼을 표시하고 페이지를 변경하면 이전 페이지로 pop()을 반환하면 버튼이 계속 나타납니다.Flutter - ListView 업데이트 후 애니메이션이 시작 부분으로 돌아 가지 않습니다.

애니메이션이 처음으로 돌아 가지 않았습니다. ListView에있는 항목을 업데이트하는 경우에도 마찬가지입니다 (예 : 항목 삭제). 항목이 삭제되었지만 애니메이션이 아닙니다.

ListView 콘텐츠를 업데이트하고있는 것 같습니다. ListView은 아닙니다.

어떻게이 문제를 해결할 수 있습니까?

해당 항목의 모든 업데이트와 함께 ListView을 (를) 파괴하려고하는데, 그 내용이 표시되지 않습니다. 이것이 문제를 해결하는 올바른 방법인지 또한 나는 모른다.

ListViewbuildTile() 함수를 통해 작성되며 해당 항목은 데이터베이스에서 가져옵니다.

아래 코드와 문제가 무엇인지 설명하는 gif를 넣습니다.

따라서, 당신은 pubspec.yamlsqflitepath_provider 의존성을 삽입해야, 아래의 코드를하려면 :

dependencies: 
    sqflite: any 
    path_provider: any 
    flutter: 
    sdk: flutter 

enter image description here

import 'package:flutter/material.dart'; 
import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'dart:ui' as ui; 

enum DialogOptionsAction { 
    cancel, 
    ok 
} 

void main() { 
    runApp(new MyApp()); 
} 

class MyApp extends StatelessWidget { 
    @override 
    Widget build(BuildContext context) { 
    return new MaterialApp(
     home: new MyHomePage(), 
     routes: <String, WidgetBuilder> { 
     '/newpage': (BuildContext context) => new NewPage(), 
     }, 
    ); 
    } 
} 

class MyHomePage extends StatefulWidget { 
    @override 
    _MyHomePageState createState() => new _MyHomePageState(); 
} 

class _MyHomePageState extends State<MyHomePage> { 
    DatabaseClient _db = new DatabaseClient(); 
    List listCategory = []; 
    List<Widget> tiles; 

    List colors = [ 
    const Color(0xFFFFA500), 
    const Color(0xFF279605), 
    const Color(0xFF005959) 
    ]; 

    createdb() async { 
    await _db.create().then(
     (data){ 
     _db.getAllCategory().then((list){ 
      setState(() { 
      this.listCategory = list; 
      }); 
     }); 
     } 
    ); 
    } 

    @override 
    void initState() { 
    super.initState(); 
    createdb();  
    } 

    void showCategoryDelete<T>({ BuildContext context, Widget child }) { 
    showDialog<T>(
     context: context, 
     child: child, 
    ) 
    .then<Null>((T value) { 
     if (value != null) { 
     setState(() { print(value); }); 
     } 
    }); 
    } 

    @override 
    Widget build(BuildContext context) { 
    List<Widget> buildTile(List list) { 
     this.tiles = []; 
     for(var dict in list) { 
     this.tiles.add(
      new ItemCategory(
      id: dict['id'], 
      category: dict['name'], 
      color: this.colors[dict['color']], 
      onPressed:() async { 
       showCategoryDelete<DialogOptionsAction>(
       context: context, 
       child: new AlertDialog(
        title: const Text('Delete Category'), 
        content: new Text(
        'Do you want to delete this category?', 
         style: new TextStyle(
         color: Colors.black26, 
         fontSize: 16.0, 
         fontFamily: "Roboto", 
         fontWeight: FontWeight.w500, 
        ) 
       ), 
        actions: <Widget>[ 
        new FlatButton(
         child: const Text('CANCEL'), 
         onPressed:() { 
         Navigator.pop(context); 
         } 
        ), 
        new FlatButton(
         child: const Text('OK'), 
         onPressed:() { 
         _db.deleteCategory(dict['id']).then(
          (list) { 
          setState(() { 
           this.listCategory = list; 
          }); 
          } 
         ); 
         Navigator.pop(context); 
         } 
        ) 
        ] 
       ) 
      ); 
      }, 
     ) 
     ); 
     } 
     return this.tiles; 
    } 
    return new Scaffold(
     appBar: new AppBar(
     title: new Text('Categories'), 
     actions: <Widget>[ 
      new IconButton(
      icon: const Icon(Icons.add), 
      color: new Color(0xFFFFFFFF), 
      onPressed:() async { 
       await Navigator.of(context).pushNamed('/newpage').then(
       (data){ 
        _db.getAllCategory().then((list){ 
        setState(() { 
         this.listCategory = list; 
        }); 
        }); 
       } 
      ); 
      } 
     ) 
     ], 
    ), 
     body: new ListView(
     padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0), 
     children: buildTile(this.listCategory) 
    ) 
    ); 
    } 
} 

class NewPage extends StatelessWidget { 
    @override 
    Widget build(BuildContext context) { 
    return new Scaffold(
     appBar: new AppBar(
     title: new Text('New Page'), 
    ), 
    ); 
    } 
} 

//Creating Database with some data and two queries 
class DatabaseClient { 
    Database db; 

    Future create() async { 
    Directory path = await getApplicationDocumentsDirectory(); 
    String dbPath = join(path.path, "database.db"); 
    db = await openDatabase(dbPath, version: 1, onCreate: this._create); 
    } 

    Future _create(Database db, int version) async { 
    await db.execute(""" 
     CREATE TABLE category (
     id INTEGER PRIMARY KEY, 
     name TEXT NOT NULL, 
     color INTEGER NOT NULL 
    )"""); 
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)"); 
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)"); 
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)"); 
    } 

    Future getAllCategory() async { 
    Directory path = await getApplicationDocumentsDirectory(); 
    String dbPath = join(path.path, "database.db"); 
    Database db = await openDatabase(dbPath); 

    List list = await db.rawQuery('SELECT * FROM category'); 
    await db.close(); 

    return list; 
    } 

    Future deleteCategory(int id) async { 
    Directory path = await getApplicationDocumentsDirectory(); 
    String dbPath = join(path.path, "database.db"); 
    Database db = await openDatabase(dbPath); 

    await db.delete('category', where: "id = ?", whereArgs: [id]); 
    List list = await db.rawQuery('SELECT * FROM category'); 

    await db.close(); 

    return list; 
    } 
} 

//Creating ListViews items 
class ItemCategory extends StatefulWidget { 
    ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key); 

    final int id; 
    final String category; 
    final Color color; 
    final VoidCallback onPressed; 

    @override 
    ItemCategoryState createState() => new ItemCategoryState(); 
} 

class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin { 
    ItemCategoryState(); 

    DatabaseClient db = new DatabaseClient(); 
    AnimationController _controller; 
    Animation<double> _animation; 
    double flingOpening; 
    bool startFling = true; 

    void initState() { 
    super.initState(); 
    _controller = new AnimationController(duration: 
     const Duration(milliseconds: 246), vsync: this); 

    _animation = new CurvedAnimation(
     parent: _controller, 
     curve: new Interval(0.0, 1.0, curve: Curves.linear), 
    ); 
    } 

    void _move(DragUpdateDetails details) { 
    final double delta = details.primaryDelta/304; 
    _controller.value -= delta; 
    } 

    void _settle(DragEndDetails details) { 
    if(this.startFling) { 
     _controller.fling(velocity: 1.0); 
     this.startFling = false; 
    } else if(!this.startFling){ 
     _controller.fling(velocity: -1.0); 
     this.startFling = true; 
    } 
    } 

    @override 
    Widget build(BuildContext context) { 
    final ui.Size logicalSize = MediaQuery.of(context).size; 
    final double _width = logicalSize.width; 
    this.flingOpening = -(48.0/_width); 

    return new GestureDetector(
     onHorizontalDragUpdate: _move, 
     onHorizontalDragEnd: _settle, 
     child: new Stack(
     children: <Widget>[ 
      new Positioned.fill(
      child: new Row(
       mainAxisAlignment: MainAxisAlignment.end, 
       children: <Widget>[ 
       new Container(
        decoration: new BoxDecoration(
        color: new Color(0xFFE57373), 
       ), 
        child: new IconButton(
        icon: new Icon(Icons.delete), 
        color: new Color(0xFFFFFFFF), 
        onPressed: widget.onPressed 
       ) 
       ), 
       ], 
      ), 
     ), 
      new SlideTransition(
      position: new Tween<Offset>(
       begin: Offset.zero, 
       end: new Offset(this.flingOpening, 0.0), 
      ).animate(_animation), 
      child: new Container(
       decoration: new BoxDecoration(
       border: new Border(
        top: new BorderSide(style: BorderStyle.solid, color: Colors.black26), 
       ), 
       color: new Color(0xFFFFFFFF), 
      ), 
       margin: new EdgeInsets.only(top: 0.0, bottom: 0.0), 
       child: new Row(
       mainAxisAlignment: MainAxisAlignment.start, 
       crossAxisAlignment: CrossAxisAlignment.center, 
       children: <Widget>[ 
        new Expanded(
        child: new Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween, 
         children: <Widget>[ 
         new Container(
          margin: new EdgeInsets.only(left: 16.0), 
          padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5), 
          child: new Row(
          children: <Widget>[ 
           new Container(
           margin: new EdgeInsets.only(right: 16.0), 
           child: new Icon(
            Icons.brightness_1, 
            color: widget.color, 
            size: 35.0, 
           ), 
          ), 
           new Text(
           widget.category, 
           style: new TextStyle(
            color: Colors.black87, 
            fontSize: 14.0, 
            fontFamily: "Roboto", 
            fontWeight: FontWeight.w500, 
           ), 
          ), 
          ], 
         ) 
         ) 
         ], 
        ), 
       ) 
       ], 
      ), 
      ) 
     ), 
     ], 
    ) 
    ); 
    } 
} 

답변

1

을 간단히를 삽입 Flutter - Widget animation status remains even after it has been removed

에 따르면 새 ItemCategory에 키 입력 (

아이들에게 key을 전달해야했습니다. 그렇지 않으면 렌더러는 제거 된 SlideTransition을 알 수 없으며 인덱스를 사용합니다.

... 
@override 
Widget build(BuildContext context) { 
    List<Widget> buildTile(List list) { 
    this.tiles = []; 
    for(var dict in list) { 
     this.tiles.add(
     new ItemCategory(
      key: new Key(dict), //new 
      id: dict['id'], 
      category: dict['name'], 
... 

enter image description here

전체 코드는 다음과 같습니다

import 'package:flutter/material.dart'; 
import 'dart:async'; 
import 'dart:io'; 
import 'package:path/path.dart'; 
import 'package:sqflite/sqflite.dart'; 
import 'package:path_provider/path_provider.dart'; 
import 'dart:ui' as ui; 

enum DialogOptionsAction { 
    cancel, 
    ok 
} 

void main() { 
    runApp(new MyApp()); 
} 

class MyApp extends StatelessWidget { 
    @override 
    Widget build(BuildContext context) { 
    return new MaterialApp(
     home: new MyHomePage(), 
     routes: <String, WidgetBuilder> { 
     '/newpage': (BuildContext context) => new NewPage(), 
     }, 
    ); 
    } 
} 

class MyHomePage extends StatefulWidget { 
    @override 
    _MyHomePageState createState() => new _MyHomePageState(); 
} 

class _MyHomePageState extends State<MyHomePage> { 
    DatabaseClient _db = new DatabaseClient(); 
    List listCategory = []; 
    List<Widget> tiles; 

    List colors = [ 
    const Color(0xFFFFA500), 
    const Color(0xFF279605), 
    const Color(0xFF005959) 
    ]; 

    createdb() async { 
    await _db.create().then(
     (data){ 
     _db.getAllCategory().then((list){ 
      setState(() { 
      this.listCategory = list; 
      }); 
     }); 
     } 
    ); 
    } 

    @override 
    void initState() { 
    super.initState(); 
    createdb();  
    } 

    void showCategoryDelete<T>({ BuildContext context, Widget child }) { 
    showDialog<T>(
     context: context, 
     child: child, 
    ) 
    .then<Null>((T value) { 
     if (value != null) { 
     setState(() { print(value); }); 
     } 
    }); 
    } 

    @override 
    Widget build(BuildContext context) { 
    List<Widget> buildTile(List list) { 
     this.tiles = []; 
     for(var dict in list) { 
     this.tiles.add(
      new ItemCategory(
      key: new Key(dict), //new 
      id: dict['id'], 
      category: dict['name'], 
      color: this.colors[dict['color']], 
      onPressed:() async { 
       showCategoryDelete<DialogOptionsAction>(
       context: context, 
       child: new AlertDialog(
        title: const Text('Delete Category'), 
        content: new Text(
        'Do you want to delete this category?', 
         style: new TextStyle(
         color: Colors.black26, 
         fontSize: 16.0, 
         fontFamily: "Roboto", 
         fontWeight: FontWeight.w500, 
        ) 
       ), 
        actions: <Widget>[ 
        new FlatButton(
         child: const Text('CANCEL'), 
         onPressed:() { 
         Navigator.pop(context); 
         } 
        ), 
        new FlatButton(
         child: const Text('OK'), 
         onPressed:() { 
         _db.deleteCategory(dict['id']).then(
          (list) { 
          setState(() { 
           this.listCategory = list; 
          }); 
          } 
         ); 
         Navigator.pop(context); 
         } 
        ) 
        ] 
       ) 
      ); 
      }, 
     ) 
     ); 
     } 
     return this.tiles; 
    } 
    return new Scaffold(
     appBar: new AppBar(
     title: new Text('Categories'), 
     actions: <Widget>[ 
      new IconButton(
      icon: const Icon(Icons.add), 
      color: new Color(0xFFFFFFFF), 
      onPressed:() async { 
       await Navigator.of(context).pushNamed('/newpage').then(
       (data){ 
        _db.getAllCategory().then((list){ 
        setState(() { 
         this.listCategory = list; 
        }); 
        }); 
       } 
      ); 
      } 
     ) 
     ], 
    ), 
     body: new ListView(
     padding: new EdgeInsets.only(top: 8.0, right: 0.0, left: 0.0), 
     children: buildTile(this.listCategory) 
    ) 
    ); 
    } 
} 

class NewPage extends StatelessWidget { 
    @override 
    Widget build(BuildContext context) { 
    return new Scaffold(
     appBar: new AppBar(
     title: new Text('New Page'), 
    ), 
    ); 
    } 
} 

//Creating Database with some data and two queries 
class DatabaseClient { 
    Database db; 

    Future create() async { 
    Directory path = await getApplicationDocumentsDirectory(); 
    String dbPath = join(path.path, "database.db"); 
    db = await openDatabase(dbPath, version: 1, onCreate: this._create); 
    } 

    Future _create(Database db, int version) async { 
    await db.execute(""" 
     CREATE TABLE category (
     id INTEGER PRIMARY KEY, 
     name TEXT NOT NULL, 
     color INTEGER NOT NULL 
    )"""); 
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo1', 0)"); 
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo2', 1)"); 
    await db.rawInsert("INSERT INTO category (name, color) VALUES ('foo3', 2)"); 
    } 

    Future getAllCategory() async { 
    Directory path = await getApplicationDocumentsDirectory(); 
    String dbPath = join(path.path, "database.db"); 
    Database db = await openDatabase(dbPath); 

    List list = await db.rawQuery('SELECT * FROM category'); 
    await db.close(); 

    return list; 
    } 

    Future deleteCategory(int id) async { 
    Directory path = await getApplicationDocumentsDirectory(); 
    String dbPath = join(path.path, "database.db"); 
    Database db = await openDatabase(dbPath); 

    await db.delete('category', where: "id = ?", whereArgs: [id]); 
    List list = await db.rawQuery('SELECT * FROM category'); 

    await db.close(); 

    return list; 
    } 
} 

//Creating ListViews items 
class ItemCategory extends StatefulWidget { 
    ItemCategory({ Key key, this.id, this.category, this.color, this.onPressed}) : super(key: key); 

    final int id; 
    final String category; 
    final Color color; 
    final VoidCallback onPressed; 

    @override 
    ItemCategoryState createState() => new ItemCategoryState(); 
} 

class ItemCategoryState extends State<ItemCategory> with TickerProviderStateMixin { 
    ItemCategoryState(); 

    DatabaseClient db = new DatabaseClient(); 
    AnimationController _controller; 
    Animation<double> _animation; 
    double flingOpening; 
    bool startFling = true; 

    void initState() { 
    super.initState(); 
    _controller = new AnimationController(duration: 
     const Duration(milliseconds: 246), vsync: this); 

    _animation = new CurvedAnimation(
     parent: _controller, 
     curve: new Interval(0.0, 1.0, curve: Curves.linear), 
    ); 
    } 

    void _move(DragUpdateDetails details) { 
    final double delta = details.primaryDelta/304; 
    _controller.value -= delta; 
    } 

    void _settle(DragEndDetails details) { 
    if(this.startFling) { 
     _controller.fling(velocity: 1.0); 
     this.startFling = false; 
    } else if(!this.startFling){ 
     _controller.fling(velocity: -1.0); 
     this.startFling = true; 
    } 
    } 

    @override 
    Widget build(BuildContext context) { 
    final ui.Size logicalSize = MediaQuery.of(context).size; 
    final double _width = logicalSize.width; 
    this.flingOpening = -(48.0/_width); 

    return new GestureDetector(
     onHorizontalDragUpdate: _move, 
     onHorizontalDragEnd: _settle, 
     child: new Stack(
     children: <Widget>[ 
      new Positioned.fill(
      child: new Row(
       mainAxisAlignment: MainAxisAlignment.end, 
       children: <Widget>[ 
       new Container(
        decoration: new BoxDecoration(
        color: new Color(0xFFE57373), 
       ), 
        child: new IconButton(
        icon: new Icon(Icons.delete), 
        color: new Color(0xFFFFFFFF), 
        onPressed: widget.onPressed 
       ) 
       ), 
       ], 
      ), 
     ), 
      new SlideTransition(
      position: new Tween<Offset>(
       begin: Offset.zero, 
       end: new Offset(this.flingOpening, 0.0), 
      ).animate(_animation), 
      child: new Container(
       decoration: new BoxDecoration(
       border: new Border(
        top: new BorderSide(style: BorderStyle.solid, color: Colors.black26), 
       ), 
       color: new Color(0xFFFFFFFF), 
      ), 
       margin: new EdgeInsets.only(top: 0.0, bottom: 0.0), 
       child: new Row(
       mainAxisAlignment: MainAxisAlignment.start, 
       crossAxisAlignment: CrossAxisAlignment.center, 
       children: <Widget>[ 
        new Expanded(
        child: new Row(
         mainAxisAlignment: MainAxisAlignment.spaceBetween, 
         children: <Widget>[ 
         new Container(
          margin: new EdgeInsets.only(left: 16.0), 
          padding: new EdgeInsets.only(right: 40.0, top: 4.5, bottom: 4.5), 
          child: new Row(
          children: <Widget>[ 
           new Container(
           margin: new EdgeInsets.only(right: 16.0), 
           child: new Icon(
            Icons.brightness_1, 
            color: widget.color, 
            size: 35.0, 
           ), 
          ), 
           new Text(
           widget.category, 
           style: new TextStyle(
            color: Colors.black87, 
            fontSize: 14.0, 
            fontFamily: "Roboto", 
            fontWeight: FontWeight.w500, 
           ), 
          ), 
          ], 
         ) 
         ) 
         ], 
        ), 
       ) 
       ], 
      ), 
      ) 
     ), 
     ], 
    ) 
    ); 
    } 
}