flutter_decision_making 1.0.2 copy "flutter_decision_making: ^1.0.2" to clipboard
flutter_decision_making: ^1.0.2 copied to clipboard

Dart package for lightweight decision making using the Analytic Hierarchy Process (AHP). Helps prioritize alternatives objectively via pairwise comparison.

example/lib/main.dart

import 'package:example/helper.dart';
import 'package:example/show_pairwise_comparison_scale_dialog.dart';
import 'package:flutter/material.dart';
import 'package:flutter_decision_making/flutter_decision_making.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
      ),

      home: const MyHomePage(title: 'Flutter Decision Making Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _criteriaController = TextEditingController();
  final _alternativeController = TextEditingController();
  late TextStyle _textStyle;
  late FlutterDecisionMaking _decisionMaking;
  late List<Criteria> _listCriteria;
  late List<Alternative> _listAlternative;
  late List<PairwiseComparisonInput<Criteria>> _inputCriteria;
  late List<PairwiseAlternativeInput> _inputAlternative;
  late Helper _helper;

  @override
  void initState() {
    super.initState();
    _decisionMaking = FlutterDecisionMaking();
    _textStyle = TextStyle(fontSize: 18);
    _listAlternative = [];
    _listCriteria = [];
    _inputCriteria = [];
    _inputAlternative = [];
    _helper = Helper();
  }

  @override
  void dispose() {
    super.dispose();
    _criteriaController.dispose();
    _alternativeController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final paddingBottom = MediaQuery.of(context).viewInsets.bottom;
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: EdgeInsets.only(
            left: 10,
            right: 10,
            top: 10,
            bottom: paddingBottom + 10,
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              /// CRITERIA YOU WANT
              _buildInputWidget(
                title: 'Criteria',
                controller: _criteriaController,
                onPressed:
                    () => _addItem(
                      _criteriaController,
                      _listCriteria,
                      (name) => Criteria(name: name),
                    ),
              ),

              /// LIST CRITERIA
              _listCriteria.isNotEmpty
                  ? Container(
                    constraints: BoxConstraints(maxHeight: 100),
                    margin: EdgeInsets.only(bottom: 10, top: 10),
                    padding: EdgeInsets.symmetric(vertical: 5, horizontal: 5),
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.black),
                    ),
                    width: double.infinity,
                    child: Scrollbar(
                      child: ListView.builder(
                        shrinkWrap: true,
                        itemCount: _listCriteria.length,
                        itemBuilder: (context, index) {
                          var data = _listCriteria[index];
                          return Text(
                            '${index + 1}. ${data.name}',
                            style: _textStyle,
                          );
                        },
                      ),
                    ),
                  )
                  : SizedBox(height: 10),

              const Divider(),

              /// ALTERNATIVE YOU HAVE
              _buildInputWidget(
                title: 'Alternative',
                controller: _alternativeController,
                onPressed:
                    () => _addItem(
                      _alternativeController,
                      _listAlternative,
                      (name) => Alternative(name: name),
                    ),
              ),

              /// LIST ALTERNATIVE
              _listAlternative.isNotEmpty
                  ? Container(
                    constraints: BoxConstraints(maxHeight: 100),
                    margin: EdgeInsets.only(bottom: 40, top: 10),
                    padding: EdgeInsets.symmetric(vertical: 5, horizontal: 5),
                    decoration: BoxDecoration(
                      border: Border.all(color: Colors.black),
                    ),
                    width: double.infinity,
                    child: Scrollbar(
                      child: ListView.builder(
                        shrinkWrap: true,
                        itemCount: _listAlternative.length,
                        itemBuilder: (context, index) {
                          var data = _listAlternative[index];
                          return Text(
                            '${index + 1}. ${data.name}',
                            style: _textStyle,
                          );
                        },
                      ),
                    ),
                  )
                  : SizedBox(height: 40),

              /// GENERATE HIERARCHY STRUCTURE & PAIRWISE MATRIX TEMPLATE
              Center(
                child: ElevatedButton(
                  onPressed: () async {
                    await _decisionMaking
                        .generateHierarchyAndPairwiseTemplate(
                          listCriteria: _listCriteria,
                          listAlternative: _listAlternative,
                        )
                        .catchError((e) {
                          if (context.mounted) {
                            _helper.showScaffoldMessenger(
                              context: context,
                              message: e.toString(),
                            );
                          }
                        });

                    setState(() {
                      /// COPY RESULT TO LIST
                      _inputCriteria =
                          _decisionMaking.listPairwiseCriteriaInput;
                      _inputAlternative =
                          _decisionMaking.listPairwiseAlternativeInput;
                    });
                  },
                  child: Text(
                    'Generate Hierarchy Structure and Pairwise Matrix Template',
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    textAlign: TextAlign.center,
                  ),
                ),
              ),

              /// PAIRWISE CRITERIA
              _inputCriteria.isNotEmpty
                  ? Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 10),
                        child: Divider(),
                      ),

                      Text(
                        'Pairwise Criteria',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 20,
                        ),
                      ),

                      /// CRITERIA ITEMS
                      ListView.builder(
                        itemCount: _inputCriteria.length,
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (context, index) {
                          var criteria = _inputCriteria[index];
                          return Row(
                            children: [
                              /// NAME
                              Text(criteria.left.name, style: _textStyle),

                              /// SELECT VALUE COMPARISON
                              Expanded(
                                child: InkWell(
                                  onTap: () {
                                    /// SHOW COMPARISON SCALE DIALOG
                                    showPairwiseComparisonScaleDialog(
                                      context,
                                      comparison:
                                          _decisionMaking
                                              .listPairwiseComparisonScale,
                                      leftItemName: criteria.left.name,
                                      rightItemName: criteria.right.name,
                                      onSelected: (scale, important) {
                                        if (scale != null &&
                                            important != null) {
                                          /// UPDATE CRITERIA VALUE
                                          _decisionMaking
                                              .updatePairwiseCriteriaValue(
                                                id: criteria.id,
                                                scale: scale,
                                                isLeftMoreImportant: important,
                                              );

                                          setState(() {
                                            /// COPY UPDATED CRITERIA VALUE
                                            _inputCriteria =
                                                _decisionMaking
                                                    .listPairwiseCriteriaInput;
                                          });
                                        }
                                      },
                                    );
                                  },
                                  child: Container(
                                    height: 40,
                                    width: double.infinity,
                                    alignment: Alignment.centerLeft,
                                    padding: EdgeInsets.symmetric(
                                      horizontal: 5,
                                    ),
                                    margin: EdgeInsets.symmetric(
                                      horizontal: 10,
                                      vertical: 5,
                                    ),
                                    decoration: BoxDecoration(
                                      border: Border.all(color: Colors.black),
                                    ),
                                    child: Text(
                                      criteria.preferenceValue != null
                                          ? '${criteria.preferenceValue?.value} - ${criteria.isLeftMoreImportant == true ? 'left item is more important' : 'right item is more important'}'
                                          : 'please select scale comparison',
                                      style: _textStyle,
                                    ),
                                  ),
                                ),
                              ),

                              /// NAME
                              Text(criteria.right.name, style: _textStyle),
                            ],
                          );
                        },
                      ),
                    ],
                  )
                  : const SizedBox(),

              /// PAIRWISE ALTERNATIVE
              _inputAlternative.isNotEmpty
                  ? Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 10),
                        child: Divider(),
                      ),

                      /// TITLE
                      Text(
                        'Pairwise Alternative',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 20,
                        ),
                      ),

                      /// ALTERNATIVE ITEMS
                      ListView.builder(
                        itemCount: _inputAlternative.length,
                        shrinkWrap: true,
                        physics: NeverScrollableScrollPhysics(),
                        itemBuilder: (context, index) {
                          final data = _inputAlternative[index];
                          return Column(
                            mainAxisSize: MainAxisSize.min,
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                data.criteria.name,
                                style: _textStyle.copyWith(
                                  fontWeight: FontWeight.bold,
                                ),
                              ),

                              ...data.alternative.map(
                                (e) => Row(
                                  children: [
                                    /// NAME
                                    Text(e.left.name, style: _textStyle),

                                    /// SELECT VALUE COMPARISON
                                    Expanded(
                                      child: InkWell(
                                        onTap: () {
                                          /// SHOW COMPARISON SCALE DIALOG
                                          showPairwiseComparisonScaleDialog(
                                            context,
                                            comparison:
                                                _decisionMaking
                                                    .listPairwiseComparisonScale,
                                            leftItemName: e.left.name,
                                            rightItemName: e.right.name,
                                            onSelected: (scale, important) {
                                              if (scale != null &&
                                                  important != null) {
                                                /// UPDATE ALTERNATIVE VALUE
                                                _decisionMaking
                                                    .updatePairwiseAlternativeValue(
                                                      id: data.criteria.id,
                                                      alternativeId: e.id,
                                                      scale: scale,
                                                      isLeftMoreImportant:
                                                          important,
                                                    );

                                                setState(() {
                                                  /// COPY UPDATED ALTERNATIVE VALUE
                                                  _inputAlternative =
                                                      _decisionMaking
                                                          .listPairwiseAlternativeInput;
                                                });
                                              }
                                            },
                                          );
                                        },
                                        child: Container(
                                          height: 40,
                                          width: double.infinity,
                                          alignment: Alignment.centerLeft,
                                          padding: EdgeInsets.symmetric(
                                            horizontal: 5,
                                          ),
                                          margin: EdgeInsets.symmetric(
                                            horizontal: 10,
                                            vertical: 5,
                                          ),
                                          decoration: BoxDecoration(
                                            border: Border.all(
                                              color: Colors.black,
                                            ),
                                          ),
                                          child: Text(
                                            e.preferenceValue != null
                                                ? '${e.preferenceValue?.value} - ${e.isLeftMoreImportant == true ? 'left item is more important' : 'right item is more important'}'
                                                : 'please select scale comparison',
                                            style: _textStyle,
                                          ),
                                        ),
                                      ),
                                    ),

                                    /// NAME
                                    Text(e.right.name, style: _textStyle),
                                  ],
                                ),
                              ),
                            ],
                          );
                        },
                      ),
                    ],
                  )
                  : const SizedBox(),

              /// GENERATE PAIRWISE MATRIX
              _decisionMaking.listPairwiseAlternativeInput.isNotEmpty &&
                      _decisionMaking.listPairwiseCriteriaInput.isNotEmpty
                  ? Padding(
                    padding: EdgeInsets.only(top: 20),
                    child: Center(
                      child: ElevatedButton(
                        onPressed: () async {
                          /// GET RESULT AHP
                          await _decisionMaking.generateResult().catchError((
                            e,
                          ) {
                            if (context.mounted) {
                              /// SHOW MESSAGE IF CATCH EXCEPTION
                              _helper.showScaffoldMessenger(
                                context: context,
                                message: e.toString(),
                              );
                            }
                          });

                          setState(() {});
                        },
                        child: Text(
                          'Generate Result',
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.bold,
                          ),
                          textAlign: TextAlign.center,
                        ),
                      ),
                    ),
                  )
                  : const SizedBox(),

              /// RESULT
              _decisionMaking.ahpResult != null
                  ? Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Padding(
                        padding: EdgeInsets.symmetric(vertical: 10),
                        child: Divider(),
                      ),

                      Text(
                        'Result',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 20,
                        ),
                      ),

                      /// RESULT ITEMS
                      ListView.builder(
                        shrinkWrap: true,
                        physics: const NeverScrollableScrollPhysics(),
                        padding: EdgeInsets.only(top: 10),
                        itemCount:
                            _decisionMaking.ahpResult?.results.length ?? 0,
                        itemBuilder: (context, index) {
                          var data = _decisionMaking.ahpResult?.results[index];
                          return Text(
                            '${data?.name}: ${data?.value}',
                            style: _textStyle,
                          );
                        },
                      ),

                      const Divider(),

                      /// RESULT CONSISTENCY CRITERIA RATIO
                      Text(
                        'criteria consistency ratio: ${_decisionMaking.ahpResult?.consistencyCriteriaRatio}',
                        style: _textStyle,
                      ),

                      const Divider(),

                      /// IS VALID CRITERIA?
                      Text(
                        'is criteria consistent: ${_decisionMaking.ahpResult?.isConsistentCriteria}',
                        style: _textStyle,
                      ),

                      const Divider(),

                      /// RESULT ALTERNATIVE RATIO
                      Text(
                        'alternative consistency ratio: ${_decisionMaking.ahpResult?.consistencyAlternativeRatio}',
                        style: _textStyle,
                      ),

                      const Divider(),

                      /// IS VALID ALTERNATIVE?
                      Text(
                        'is alternative consistent: ${_decisionMaking.ahpResult?.isConsistentAlternative}',
                        style: _textStyle,
                      ),

                      const Divider(),

                      /// NOTE WILL BE DISPLAYED IF THE CRITERIA OR ALTERNATIVES ARE INVALID
                      _decisionMaking.ahpResult?.note != null
                          ? Text(
                            _decisionMaking.ahpResult!.note!,
                            style: _textStyle,
                          )
                          : const SizedBox(),
                    ],
                  )
                  : const SizedBox(),
            ],
          ),
        ),
      ),
    );
  }

  /// INPUT FOR CRITERIA OR ALTERNATIVE
  Widget _buildInputWidget({
    required String title,
    required TextEditingController controller,
    required Function() onPressed,
  }) {
    return Row(
      children: [
        Text(title, style: _textStyle),

        Expanded(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 10),
            child: TextFormField(
              controller: controller,
              decoration: InputDecoration(border: OutlineInputBorder()),
            ),
          ),
        ),

        ElevatedButton(onPressed: onPressed, child: Text('Add')),
      ],
    );
  }

  /// ADD ITEM TO CRITERIA OR ALTERNATIVE
  void _addItem<T>(
    TextEditingController controller,
    List<T> items,
    T Function(String name) createItem,
  ) {
    final value = controller.text.trim();
    if (value.isNotEmpty) {
      setState(() {
        items.add(createItem(value));
        controller.clear();
      });
    }
  }
}
1
likes
0
points
194
downloads

Publisher

unverified uploader

Weekly Downloads

Dart package for lightweight decision making using the Analytic Hierarchy Process (AHP). Helps prioritize alternatives objectively via pairwise comparison.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on flutter_decision_making