【问题标题】:How to navigate text to one widget and not to all widgets on the screen at the same time using Provider?如何使用 Provider 将文本导航到一个小部件而不是同时导航到屏幕上的所有小部件?
【发布时间】:2021-05-18 20:23:55
【问题描述】:

我正在开发一个具有多个小部件('星期一'、'星期二'等)的屏幕的计划应用程序。当我点击一个小部件时,我应该能够在弹出屏幕上使用 TextField 并进行导航文本到我点击的小部件。现在的问题是提供者同时将文本导航到所有小部件,而不是我点击的一个。我怎么能解决这个问题?感谢您的帮助

这是一个计划屏幕

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:my_planner_app/weekday_card.dart';

class PlannerScreen extends StatefulWidget {
  static const String id = 'planner_screen';

  @override
  _PlannerScreenState createState() => _PlannerScreenState();
}

class _PlannerScreenState extends State<PlannerScreen>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

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

    controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);
    animation = ColorTween(begin: Colors.grey[800], end: Colors.white)
        .animate(controller);
    controller.forward();
    controller.addListener(() {
      setState(() {});
    });
  }

  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;

    final double itemHeight = (size.height - 24) / 2;
    final double itemWidth = size.width / 2;
    return Scaffold(
      backgroundColor: Color(0xFFcf9e9f),
      body: Container(
        child: GridView(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
            childAspectRatio: (itemWidth / itemHeight),
          ),
          children: <Widget>[
            WeekDayCard(
              text: '',
            ),
            WeekDayCard(text: 'Monday'),
            WeekDayCard(text: 'Tuesday'),
            WeekDayCard(text: 'Wednesday'),
            WeekDayCard(text: 'Thursday'),
            WeekDayCard(text: 'Friday'),
            WeekDayCard(text: 'Saturday'),
            WeekDayCard(text: 'Sunday'),
            WeekDayCard(text: 'Notes'),
          ],
        ),
      ),
    );
  }
}

这是关联的小部件


        import 'package:flutter/material.dart';
   import 'package:my_planner_app/screens/addPlan_screen.dart';
import 'package:provider/provider.dart';
import 'package:my_planner_app/widgets/plan_widget.dart';

class WeekDayCard extends StatelessWidget {
  WeekDayCard({@required this.text, this.name});
  final String name;
  final String text;
  @override
  Widget build(BuildContext context) {
    return Consumer<MyProvider>(builder: (context, myProvider, child) {
      return Card(
        color: Color(0xFFFEEFCD),
        elevation: 10,
        child: Column(
          children: [
            Text(text),
            Text(Provider.of<MyProvider>(context).name),
            Expanded(
              child: InkWell(
                onTap: () {
                  showModalBottomSheet(
                    backgroundColor: Color(0xFFFEEFCD),
                    context: context,
                    builder: (context) => AddPlanScreen(),
                  );
                },
              ),
            ),
          ],
        ),
      );
    });
  }
}

这是关联弹出的AddScreen

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:my_planner_app/widgets/plan_widget.dart';

class AddPlanScreen extends StatelessWidget {
  static String name;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: TextFormField(
            onChanged: (text) {
              name = text;
            },
            decoration: InputDecoration(
              border: InputBorder.none,
            ),
            minLines: 10,
            maxLines: 30,
            autocorrect: false,
          ),
        ),
        FlatButton(
          onPressed: () {
            print(name);
            Provider.of<MyProvider>(context, listen: false).setName(name);
          },
          color: Colors.blue,
        ),
      ],
    );
  }
}

提供者

import 'package:flutter/material.dart';

class MyProvider extends ChangeNotifier {
  String _name = '';
  String get name => _name;
  void setName(String newString) {
    _name = newString;
    print(_name);
    notifyListeners();
  }
}

ChangeNotifierProvider 放在 MaterialApp 之前


void main() {
  runApp(MyPlanner());
}

class MyPlanner extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyProvider(),
      child: MaterialApp(
        theme: ThemeData(fontFamily: 'IndieFlower'),
        initialRoute: WelcomeScreen.id,
        routes: {
          WelcomeScreen.id: (context) => WelcomeScreen(),
          RegisterScreen.id: (context) => RegisterScreen(),
          LogInScreen.id: (context) => LogInScreen(),
          PlannerScreen.id: (context) => PlannerScreen(),
        },
      ),
    );
  }
}

【问题讨论】:

  • 如果您提供的代码示例是独立的,会更容易提供帮助。 (⇒AddPlanScreen)
  • @Thierry 谢谢,添加了单独的代码。对不起,我是新来的,仍然需要学习如何更好地格式化

标签: flutter dart flutter-provider


【解决方案1】:

快速解决方案一

(与您当前的代码库保持一致)

您的ChangeNotifier 应该保留Map&lt;String, String&gt; 而不是String,每个工作日一个条目。

提供者

class MyProvider extends ChangeNotifier {
  Map<String, String> _names = {};
  
  String name(String key) => _names[key];
  
  void setName(String key, String newString) {
    _names[key] = newString;
    notifyListeners();
  }
}

然后,您将需要进行以下更改:

平日卡

代替Text(Provider.of&lt;MyProvider&gt;(context).name),使用密钥text 获取当天的nameText(Provider.of&lt;MyProvider&gt;(context).name(text) ?? '')

打开模态底页时,传递工作日名称:AddPlanScreen(weekdayName: text)

class WeekDayCard extends StatelessWidget {
  WeekDayCard({@required this.text, this.name});
  final String name;
  final String text;
  @override
  Widget build(BuildContext context) {
    return Consumer<MyProvider>(builder: (context, myProvider, child) {
      return Card(
        color: Color(0xFFFEEFCD),
        elevation: 10,
        child: Column(
          children: [
            Text(text),
            Text(Provider.of<MyProvider>(context).name(text) ?? ''),
            Expanded(
              child: InkWell(
                onTap: () {
                  showModalBottomSheet(
                    backgroundColor: Color(0xFFFEEFCD),
                    context: context,
                    builder: (context) => AddPlanScreen(weekdayName: text),
                  );
                },
              ),
            ),
          ],
        ),
      );
    });
  }
}

添加计划屏幕

  1. 首先,它应该是StatefulWidget,而不是带有静态变量的StatelessWidget
  2. 它应该接受一个参数weekdayName
  3. 设置名称时,应通过weekdayName作为键:Provider.of&lt;MyProvider&gt;(context, listen: false).setName(widget.weekdayName, name);
class AddPlanScreen extends StatefulWidget {
  final String weekdayName;

  const AddPlanScreen({Key key, this.weekdayName}) : super(key: key);

  @override
  _AddPlanScreenState createState() => _AddPlanScreenState();
}

class _AddPlanScreenState extends State<AddPlanScreen> {
  String name;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: TextFormField(
            onChanged: (text) {
              name = text;
            },
            decoration: InputDecoration(
              border: InputBorder.none,
            ),
            minLines: 10,
            maxLines: 30,
            autocorrect: false,
          ),
        ),
        ElevatedButton(
          onPressed: () {
            Provider.of<MyProvider>(context, listen: false)
                .setName(widget.weekdayName, name);
          },
          child: Text('UPDATE'),
        ),
      ],
    );
  }
}

对解决方案 2 的进一步重构

在这个解决方案中,我将使用Riverpod 而不是 Provider。这两个包都是由 Remi ROUSSELET 创作的。 Riverpod 有多种口味,我更喜欢hooks_riverpod

我保持了相同的结构:

  • MyPlannerMaterialApp。我封装在 Riverpod 的 ProviderScope
  • PlannerScreen 是主屏幕。现在是StatelessWidget。它还可以响应显示 4x2 或 2x4 的网格,具体取决于方向
  • WeekdayCard 是一个HookWidget,它需要一个weekday 并监听planProvider
  • AddPlanScreenHookWidget。这允许在不需要StatefulWidget 的情况下维护 TextEditingController。它还使用context.read 更改planProvider 的状态

提供者呢?

final planProvider = StateProvider.family<String, int>((ref, weekday) => '');

这是一个使用 .family 提供者修饰符的简单 StateProvider。 (more info)

这使我们能够听取和修改特定工作日的计划:

听:

final String plan = useProvider(planProvider(weekday)).state;

修改:

context.read(planProvider(weekday)).state = plan;

完整源代码

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

void main() {
  runApp(MyPlanner());
}

class MyPlanner extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(fontFamily: 'IndieFlower'),
        initialRoute: PlannerScreen.id,
        routes: {
          // WelcomeScreen.id: (context) => WelcomeScreen(),
          // RegisterScreen.id: (context) => RegisterScreen(),
          // LogInScreen.id: (context) => LogInScreen(),
          PlannerScreen.id: (context) => PlannerScreen(),
        },
      ),
    );
  }
}

// SCREENS

class PlannerScreen extends StatelessWidget {
  static const String id = 'planner_screen';

  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFD04E43),
      body: LayoutBuilder(
        builder: (_, constraints) {
          final mainAxisCount =
              MediaQuery.of(context).orientation == Orientation.landscape
                  ? 2
                  : 4;
          final crossAxisCount = mainAxisCount == 2 ? 4 : 2;
          final aspectRatio =
              constraints.biggest.aspectRatio * mainAxisCount / crossAxisCount;
          return GridView.count(
            crossAxisCount: crossAxisCount,
            childAspectRatio: aspectRatio,
            children: weekdayNames.keys
                .map((weekday) => WeekdayCard(weekday: weekday))
                .toList(),
          );
        },
      ),
    );
  }
}

// WIDGETS

class WeekdayCard extends HookWidget {
  final int weekday;

  WeekdayCard({@required this.weekday});

  @override
  Widget build(BuildContext context) {
    final plan = useProvider(planProvider(weekday)).state;
    return InkWell(
      onTap: () {
        showModalBottomSheet(
          backgroundColor: Color(0xFFAFBDB8),
          barrierColor: Colors.black38,
          context: context,
          builder: (context) => AddPlanScreen(weekday: weekday),
        );
      },
      child: Card(
        color: Color(0xFFAFBDB8),
        elevation: 10,
        child: Column(
          children: [
            Text(weekdayNames[weekday]),
            Text(plan),
          ],
        ),
      ),
    );
  }
}

class AddPlanScreen extends HookWidget {
  final int weekday;

  const AddPlanScreen({Key key, this.weekday}) : super(key: key);

  void submit(BuildContext context, String plan) {
    context.read(planProvider(weekday)).state = plan;
    Navigator.pop(context);
  }

  @override
  Widget build(BuildContext context) {
    final _controller = useTextEditingController(text: '');
    return Container(
      padding: EdgeInsets.all(16.0),
      alignment: Alignment.center,
      child: Column(
        children: [
          Text('What are you planning for ${weekdayNames[weekday]}?'),
          const SizedBox(height: 16.0),
          TextFormField(
            controller: _controller,
            decoration: InputDecoration(
              enabledBorder: OutlineInputBorder(
                borderRadius: new BorderRadius.circular(25.0),
                borderSide: BorderSide(
                  color: Colors.black45,
                  width: 2.0,
                ),
              ),
              focusedBorder: OutlineInputBorder(
                borderRadius: new BorderRadius.circular(25.0),
                borderSide: BorderSide(
                  color: Color(0xFFD04E43),
                  width: 2.0,
                ),
              ),
            ),
            autofocus: true,
            onEditingComplete: () => submit(context, _controller.text),
          ),
          const SizedBox(height: 16.0),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              primary: Color(0xFF548279),
              onPrimary: Colors.white,
            ),
            onPressed: () => submit(context, _controller.text),
            child: Text('UPDATE'),
          ),
        ],
      ),
    );
  }
}

// PROVIDERS

final planProvider = StateProvider.family<String, int>((ref, weekday) => '');

// DOMAIN

const weekdayNames = {
  0: 'Notes',
  DateTime.monday: 'Monday',
  DateTime.tuesday: 'Tuesday',
  DateTime.wednesday: 'Wednesday',
  DateTime.thursday: 'Thursday',
  DateTime.friday: 'Friday',
  DateTime.saturday: 'Saturday',
  DateTime.sunday: 'Sunday',
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-22
    • 1970-01-01
    • 2021-07-02
    • 2023-03-31
    • 2021-12-19
    • 1970-01-01
    • 2018-12-30
    • 1970-01-01
    相关资源
    最近更新 更多